summaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-extended/vhost-device-console
diff options
context:
space:
mode:
Diffstat (limited to 'meta-egvirt/recipes-extended/vhost-device-console')
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md4
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.lock1343
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml24
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE0
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause0
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md112
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs376
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs88
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs63
-rwxr-xr-x[-rw-r--r--]meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs1431
-rwxr-xr-xmeta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/virtio_console.rs60
-rw-r--r--meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc361
-rw-r--r--meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb6
13 files changed, 3095 insertions, 773 deletions
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md
index 51d3f040..6c84888c 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md
@@ -1,5 +1,5 @@
# Changelog
-## [Unreleased]
+## Unreleased
### Added
@@ -9,7 +9,7 @@
### Deprecated
-## [0.1.0]
+## v0.1.0
First release
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.lock b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.lock
new file mode 100755
index 00000000..1486b386
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.lock
@@ -0,0 +1,1343 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8"
+
+[[package]]
+name = "arc-swap"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+
+[[package]]
+name = "assert_matches"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "byte_conv"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "649972315d4931137a26fc2bf3ca95ee257ad796a5b57bdeb04205c91a4b5780"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.5.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+
+[[package]]
+name = "console"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
+dependencies = [
+ "encode_unicode",
+ "lazy_static",
+ "libc",
+ "unicode-width",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "crossterm"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
+dependencies = [
+ "bitflags 2.6.0",
+ "crossterm_winapi",
+ "libc",
+ "mio",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "embedded-can"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438"
+dependencies = [
+ "nb",
+]
+
+[[package]]
+name = "encode_unicode"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+
+[[package]]
+name = "enumn"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "env_filter"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
+[[package]]
+name = "epoll"
+version = "4.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "indexmap"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+dependencies = [
+ "hermit-abi 0.4.0",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memoffset"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+
+[[package]]
+name = "neli"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1100229e06604150b3becd61a4965d5c70f3be1759544ea7274166f4be41ef43"
+dependencies = [
+ "byteorder",
+ "libc",
+ "log",
+ "neli-proc-macros",
+]
+
+[[package]]
+name = "neli-proc-macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4"
+dependencies = [
+ "either",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+ "memoffset",
+ "pin-utils",
+]
+
+[[package]]
+name = "nix"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
+dependencies = [
+ "bitflags 2.6.0",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "queues"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0"
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+dependencies = [
+ "bitflags 2.6.0",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "relative-path"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
+
+[[package]]
+name = "rstest"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "rstest_macros",
+ "rustc_version",
+]
+
+[[package]]
+name = "rstest_macros"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e1711e7d14f74b12a58411c542185ef7fb7f2e7f8ee6e2940a883628522b42"
+dependencies = [
+ "cfg-if",
+ "glob",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "relative-path",
+ "rustc_version",
+ "syn 2.0.77",
+ "unicode-ident",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36"
+dependencies = [
+ "bitflags 2.6.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "socketcan"
+version = "3.4.0-pre.0"
+source = "git+https://github.com/socketcan-rs/socketcan-rs.git?rev=f004ee91e142a37fea36c5d719a57852c7076e87#f004ee91e142a37fea36c5d719a57852c7076e87"
+dependencies = [
+ "bitflags 1.3.2",
+ "byte_conv",
+ "embedded-can",
+ "hex",
+ "itertools",
+ "libc",
+ "log",
+ "nb",
+ "neli",
+ "nix 0.26.4",
+ "socket2",
+ "thiserror",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+
+[[package]]
+name = "toml_edit"
+version = "0.22.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "v4l2r"
+version = "0.0.1"
+source = "git+https://github.com/Gnurou/v4l2r?rev=110fd77#110fd773d15c787cbc6c2ded1bf8459c8df89f72"
+dependencies = [
+ "anyhow",
+ "bitflags 2.6.0",
+ "enumn",
+ "log",
+ "nix 0.27.1",
+ "thiserror",
+]
+
+[[package]]
+name = "vhost"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-device-can"
+version = "0.1.0"
+dependencies = [
+ "assert_matches",
+ "clap",
+ "env_logger 0.11.5",
+ "log",
+ "queues",
+ "socketcan",
+ "thiserror",
+ "vhost",
+ "vhost-user-backend",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-device-console"
+version = "0.1.0"
+dependencies = [
+ "assert_matches",
+ "clap",
+ "console",
+ "crossterm",
+ "env_logger 0.10.2",
+ "epoll",
+ "log",
+ "nix 0.26.4",
+ "queues",
+ "thiserror",
+ "vhost",
+ "vhost-user-backend",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-device-video"
+version = "0.1.0"
+dependencies = [
+ "assert_matches",
+ "bitflags 2.6.0",
+ "clap",
+ "env_logger 0.11.5",
+ "epoll",
+ "futures-executor",
+ "libc",
+ "log",
+ "num_enum",
+ "rstest",
+ "tempfile",
+ "thiserror",
+ "v4l2r",
+ "vhost",
+ "vhost-user-backend",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vhost-user-backend"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f0ffb1dd8e00a708a0e2c32d5efec5812953819888591fff9ff68236b8a5096"
+dependencies = [
+ "libc",
+ "log",
+ "vhost",
+ "virtio-bindings",
+ "virtio-queue",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "virtio-bindings"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68d0df4f5ad79b1dc81b5913ac737e24a84dcd5100f36ed953a1faec18aba241"
+
+[[package]]
+name = "virtio-queue"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07d8406e7250c934462de585d8f2d2781c31819bca1fbb7c5e964ca6bbaabfe8"
+dependencies = [
+ "log",
+ "virtio-bindings",
+ "vm-memory",
+ "vmm-sys-util",
+]
+
+[[package]]
+name = "vm-memory"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3aba5064cc5f6f7740cddc8dae34d2d9a311cac69b60d942af7f3ab8fc49f4"
+dependencies = [
+ "arc-swap",
+ "bitflags 2.6.0",
+ "libc",
+ "thiserror",
+ "vmm-sys-util",
+ "winapi",
+]
+
+[[package]]
+name = "vmm-sys-util"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
+dependencies = [
+ "memchr",
+]
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml
index c2f9f71e..aa520360 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "vhost-device-console"
-version = "0.0.1"
+version = "0.1.0"
authors = ["Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>"]
description = "vhost console backend device"
repository = "https://github.com/rust-vmm/vhost-device"
@@ -16,20 +16,22 @@ xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"]
[dependencies]
console = "0.15.7"
+crossterm = "0.27.0"
+nix = "0.26.4"
queues = "1.0.2"
-clap = { version = "4.2.5", features = ["derive"] }
+clap = { version = "4.4", features = ["derive"] }
env_logger = "0.10"
-libc = "0.2"
+epoll = "4.3"
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"
+vhost = { version = "0.11", features = ["vhost-user-backend"] }
+vhost-user-backend = "0.15"
+virtio-bindings = "0.2.2"
+virtio-queue = "0.12"
+vm-memory = "0.14.1"
+vmm-sys-util = "0.12"
[dev-dependencies]
assert_matches = "1.5"
-virtio-queue = { version = "0.9", features = ["test-utils"] }
-vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] }
+virtio-queue = { version = "0.12", features = ["test-utils"] }
+vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic"] }
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE
index d6456956..d6456956 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause
index dd975d98..dd975d98 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md
index b2c92440..c9eba8a7 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md
@@ -1,9 +1,32 @@
# vhost-device-console - Console emulation backend daemon
## Description
-This program is a vhost-user backend that emulates a VirtIO Consolw device.
+
+This program is a vhost-user backend that emulates a VirtIO Console device.
+The device's binary takes as parameters a socket path, a socket number which
+is the number of connections, commonly used across all vhost-devices to
+communicate with the vhost-user frontend devices, and the backend type
+"nested" or "network".
+
+The "nested" backend allows input/output to the guest console through the
+current terminal.
+
+The "network" backend creates a local TCP port (specified on vhost-device-console
+arguments) and allows input/output to the guest console via that socket.
+
+This program is tested with QEMU's `vhost-user-device-pci` device.
+Examples' section below.
+
+## Staging Device
+This device will be in `staging` until we complete the following steps:
+- [ ] Increase test coverage
+- [ ] Support VIRTIO_CONSOLE_F_SIZE feature (optional)
+- [ ] Support VIRTIO_CONSOLE_F_EMERG_WRITE feature (optional)
## Synopsis
+```text
+vhost-device-console --socket-path=<SOCKET_PATH>
+```
## Options
@@ -18,8 +41,95 @@ This program is a vhost-user backend that emulates a VirtIO Consolw device.
Location of vhost-user Unix domain sockets, this path will be suffixed with
0,1,2..socket_count-1.
+.. option:: -p, --tcp-port=PORT_NUMBER
+
+ The localhost's port to be used for each guest, this part will be increased with
+ 0,1,2..socket_count-1.
+
+-- option:: -b, --backend=nested|network
+
+ The backend type vhost-device-console to be used. The current implementation
+ supports two types of backends: "nested", "network" (described above).
+ Note: The nested backend is selected by default and can be used only when
+ socket_count equals 1.
+
+## Limitations
+
+This device is still work-in-progress (WIP). The current version has been tested
+with VIRTIO_CONSOLE_F_MULTIPORT, but only for one console (`max_nr_ports = 1`).
+Also it does not yet support the VIRTIO_CONSOLE_F_EMERG_WRITE and
+VIRTIO_CONSOLE_F_SIZE features.
+
+## Features
+
+The current device gives access to multiple QEMU guest by providing a login prompt
+either by connecting to a localhost server port (network backend) or by creating an
+nested command prompt in the current terminal (nested backend). This prompt appears
+as soon as the guest is fully booted and gives the ability to user run command as a
+in regular terminal.
+
## Examples
+### Dependencies
+For testing the device the required dependencies are:
+- Linux:
+ - Set `CONFIG_VIRTIO_CONSOLE=y`
+- QEMU (optional):
+ - A new vhost-user-console device has been implemented in the following repo:
+ - https://github.com/virtualopensystems/qemu/tree/vhu-console-rfc
+
+
+### Test the device
+
+The daemon should be started first:
+```shell
+host# vhost-device-console --socket-path=/tmp/console.sock --socket-count=1 \
+ --tcp-port=12345 --backend=network
+```
+>Note: In case the backend is "nested" there is no need to provide
+ "--socket-count" and "--tcp-port" parameters.
+
+The QEMU invocation needs to create a chardev socket the device can
+use to communicate as well as share the guests memory over a memfd.
+
+There are two option for running QEMU with vhost-device-console:
+
+1) Using `vhost-user-console-pci`:
+```text
+host# qemu-system \
+ <normal QEMU options> \
+ -machine <machine options>,memory-backend=mem0 \
+ -object memory-backend-memfd,id=mem0,size=<Guest RAM size> \ # size == -m size
+ -chardev socket,path=/tmp/console.sock0,id=con \
+ -device vhost-user-console-pci,chardev=con0,id=console \
+ ...
+```
+
+> Note: For testing this scenario the reader needs to clone the QEMU version from the following repo
+> which implements `vhost-user-console` device.
+> - https://github.com/virtualopensystems/qemu/tree/vhu-console-rfc
+
+2) Using `vhost-user-device-pci`:
+```text
+host# qemu-system \
+ <normal QEMU options> \
+ -machine <machine options>,memory-backend=mem0 \
+ -object memory-backend-memfd,id=mem0,size=<Guest RAM size> \ # size == -m size
+ -chardev socket,id=con0,path=/tmp/console.sock0 \
+ -device vhost-user-device-pci,chardev=con0,virtio-id=3,num_vqs=4,config_size=12 \
+ ...
+```
+
+Eventually, the user can connect to the console by running:
+```test
+host# stty -icanon -echo && nc localhost 12345 && stty echo
+```
+
+>Note: `stty -icanon -echo` is used to force the tty layer to disable buffering and send / receive each character individually. After closing the connection please run `stty echo` so character are printed back on the local terminal console.
+
+>Note: In case the backend is "nested" a nested terminal will be shown into
+ vhost-device-console terminal space.
+
## License
This project is licensed under either of
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs
index 15b50a38..62d6745c 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs
@@ -1,22 +1,22 @@
// VIRTIO CONSOLE Emulation via vhost-user
//
-// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
use log::{error, info, warn};
-use std::process::exit;
+use std::any::Any;
+use std::collections::HashMap;
+use std::path::PathBuf;
use std::sync::{Arc, RwLock};
-use std::thread::{spawn, JoinHandle};
+use std::thread::Builder;
-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::console::{ConsoleController};
+use crate::console::{BackendType, ConsoleController};
use crate::vhu_console::VhostUserConsoleBackend;
pub(crate) type Result<T> = std::result::Result<T, Error>;
@@ -26,186 +26,288 @@ pub(crate) type Result<T> = std::result::Result<T, Error>;
pub(crate) enum Error {
#[error("Invalid socket count: {0}")]
SocketCountInvalid(usize),
- #[error("Failed to join threads")]
- FailedJoiningThreads,
- #[error("Could not create console controller: {0}")]
- CouldNotCreateConsoleController(crate::console::Error),
#[error("Could not create console backend: {0}")]
CouldNotCreateBackend(crate::vhu_console::Error),
#[error("Could not create daemon: {0}")]
CouldNotCreateDaemon(vhost_user_backend::Error),
-}
-
-#[derive(Parser, Debug)]
-#[clap(author, version, about, long_about = None)]
-struct ConsoleArgs {
- /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
- #[clap(short, long)]
- socket_path: String,
-
- /// A console device name to be used for reading (ex. vconsole, console0, console1, ... etc.)
- #[clap(short = 'i', long)]
- console_path: String,
-
- /// Number of guests (sockets) to connect to.
- #[clap(short = 'c', long, default_value_t = 1)]
- socket_count: u32,
+ #[error("Fatal error: {0}")]
+ ServeFailed(vhost_user_backend::Error),
+ #[error("Thread `{0}` panicked")]
+ ThreadPanic(String, Box<dyn Any + Send>),
+ #[error("Error using multiple sockets with Nested backend")]
+ WrongBackendSocket,
}
#[derive(PartialEq, Debug)]
-struct ConsoleConfiguration {
- socket_path: String,
- socket_count: u32,
- console_path: String,
+pub struct VuConsoleConfig {
+ pub(crate) socket_path: PathBuf,
+ pub(crate) backend: BackendType,
+ pub(crate) tcp_port: String,
+ pub(crate) socket_count: u32,
}
-impl TryFrom<ConsoleArgs> for ConsoleConfiguration {
- type Error = Error;
+impl VuConsoleConfig {
+ pub fn generate_socket_paths(&self) -> Vec<PathBuf> {
+ let socket_file_name = self
+ .socket_path
+ .file_name()
+ .expect("socket_path has no filename.");
+ let socket_file_parent = self
+ .socket_path
+ .parent()
+ .expect("socket_path has no parent directory.");
+
+ let make_socket_path = |i: u32| -> PathBuf {
+ let mut file_name = socket_file_name.to_os_string();
+ file_name.push(std::ffi::OsStr::new(&i.to_string()));
+ socket_file_parent.join(&file_name)
+ };
- fn try_from(args: ConsoleArgs) -> Result<Self> {
+ (0..self.socket_count).map(make_socket_path).collect()
+ }
- if args.socket_count == 0 {
- return Err(Error::SocketCountInvalid(0));
- }
+ pub fn generate_tcp_addrs(&self) -> Vec<String> {
+ let tcp_port_base = self.tcp_port.clone();
- let console_path = args.console_path.trim().to_string();
+ let make_tcp_port = |i: u32| -> String {
+ let port_num: u32 = tcp_port_base.clone().parse().unwrap();
+ "127.0.0.1:".to_owned() + &(port_num + i).to_string()
+ };
- Ok(ConsoleConfiguration {
- socket_path: args.socket_path,
- socket_count: args.socket_count,
- console_path,
- })
+ (0..self.socket_count).map(make_tcp_port).collect()
}
}
-fn start_backend(args: ConsoleArgs) -> Result<()> {
-
- println!("start_backend function!\n");
-
- let config = ConsoleConfiguration::try_from(args).unwrap();
- let mut handles = Vec::new();
+// This is the public API through which an external program starts the
+/// vhost-device-console backend server.
+pub(crate) fn start_backend_server(
+ socket: PathBuf,
+ tcp_addr: String,
+ backend: BackendType,
+) -> Result<()> {
+ loop {
+ let controller = ConsoleController::new(backend);
+ let arc_controller = Arc::new(RwLock::new(controller));
+ let vu_console_backend = Arc::new(RwLock::new(
+ VhostUserConsoleBackend::new(arc_controller).map_err(Error::CouldNotCreateBackend)?,
+ ));
+
+ let mut daemon = VhostUserDaemon::new(
+ String::from("vhost-device-console-backend"),
+ vu_console_backend.clone(),
+ GuestMemoryAtomic::new(GuestMemoryMmap::new()),
+ )
+ .map_err(Error::CouldNotCreateDaemon)?;
+
+ let vring_workers = daemon.get_epoll_handlers();
+ vu_console_backend
+ .read()
+ .unwrap()
+ .set_vring_worker(&vring_workers[0]);
+
+ // Start the corresponding console thread
+ let read_handle = if backend == BackendType::Nested {
+ VhostUserConsoleBackend::start_console_thread(&vu_console_backend)
+ } else {
+ VhostUserConsoleBackend::start_tcp_console_thread(&vu_console_backend, tcp_addr.clone())
+ };
- for _ in 0..config.socket_count {
- let socket = config.socket_path.to_owned();
- let console_path = config.console_path.to_owned();
+ daemon.serve(&socket).map_err(Error::ServeFailed)?;
- let handle: JoinHandle<Result<()>> = spawn(move || loop {
- // A separate thread is spawned for each socket and console 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.
+ // Kill console input thread
+ vu_console_backend.read().unwrap().kill_console_thread();
- let controller =
- ConsoleController::new(console_path.clone()).map_err(Error::CouldNotCreateConsoleController)?;
- let arc_controller = Arc::new(RwLock::new(controller));
- let vu_console_backend = Arc::new(RwLock::new(
- VhostUserConsoleBackend::new(arc_controller).map_err(Error::CouldNotCreateBackend)?,
- ));
+ // Wait for read thread to exit
+ match read_handle.join() {
+ Ok(_) => info!("The read thread returned successfully"),
+ Err(e) => warn!("The read thread returned the error: {:?}", e),
+ }
+ }
+}
- let mut daemon = VhostUserDaemon::new(
- String::from("vhost-device-console-backend"),
- vu_console_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_console_backend.read()
- .unwrap()
- .set_vring_worker(&vring_workers[0]);
- VhostUserConsoleBackend::start_console_thread(&vu_console_backend);
-
- 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_console_backend.read().unwrap().exit_event.write(1).unwrap();
- });
-
- handles.push(handle);
+pub fn start_backend(config: VuConsoleConfig) -> Result<()> {
+ let mut handles = HashMap::new();
+ let (senders, receiver) = std::sync::mpsc::channel();
+ let tcp_addrs = config.generate_tcp_addrs();
+ let backend = config.backend;
+
+ for (thread_id, (socket, tcp_addr)) in config
+ .generate_socket_paths()
+ .into_iter()
+ .zip(tcp_addrs.iter())
+ .enumerate()
+ {
+ let tcp_addr = tcp_addr.clone();
+ info!("thread_id: {}, socket: {:?}", thread_id, socket);
+
+ let name = format!("vhu-console-{}", tcp_addr);
+ let sender = senders.clone();
+ let handle = Builder::new()
+ .name(name.clone())
+ .spawn(move || {
+ let result = std::panic::catch_unwind(move || {
+ start_backend_server(socket, tcp_addr.to_string(), backend)
+ });
+
+ // Notify the main thread that we are done.
+ sender.send(thread_id).unwrap();
+
+ result.map_err(|e| Error::ThreadPanic(name, e))?
+ })
+ .unwrap();
+ handles.insert(thread_id, handle);
}
- for handle in handles {
- handle.join().map_err(|_| Error::FailedJoiningThreads)??;
+ while !handles.is_empty() {
+ let thread_id = receiver.recv().unwrap();
+ handles
+ .remove(&thread_id)
+ .unwrap()
+ .join()
+ .map_err(std::panic::resume_unwind)
+ .unwrap()?;
}
Ok(())
}
-pub(crate) fn console_init() {
- env_logger::init();
- println!("Console_init function!");
- if let Err(e) = start_backend(ConsoleArgs::parse()) {
- error!("{e}");
- exit(1);
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
+ use crate::ConsoleArgs;
+ use assert_matches::assert_matches;
#[test]
- fn test_console_configuration_try_from_valid_args() {
+ fn test_console_valid_configuration_nested() {
let args = ConsoleArgs {
- socket_path: String::from("/path/to/socket"),
- console_path: String::from("vconsole"),
- socket_count: 3,
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Nested,
+ tcp_port: String::from("12345"),
+ socket_count: 1,
};
- let result = ConsoleConfiguration::try_from(args);
+ assert!(VuConsoleConfig::try_from(args).is_ok());
+ }
- assert!(result.is_ok());
+ #[test]
+ fn test_console_invalid_configuration_nested_1() {
+ let args = ConsoleArgs {
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Nested,
+ tcp_port: String::from("12345"),
+ socket_count: 0,
+ };
- let config = result.unwrap();
- assert_eq!(config.socket_path, "/path/to/socket");
- assert_eq!(config.console_path, "vconsole");
- assert_eq!(config.socket_count, 3);
+ assert_matches!(
+ VuConsoleConfig::try_from(args),
+ Err(Error::SocketCountInvalid(0))
+ );
}
#[test]
- fn test_console_configuration_try_from_invalid_args() {
- // Test with socket_count = 0
- let args_invalid_count = ConsoleArgs {
- socket_path: String::from("/path/to/socket"),
- console_path: String::from("vconsole"),
- socket_count: 0,
+ fn test_console_invalid_configuration_nested_2() {
+ let args = ConsoleArgs {
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Nested,
+ tcp_port: String::from("12345"),
+ socket_count: 2,
+ };
+
+ assert_matches!(
+ VuConsoleConfig::try_from(args),
+ Err(Error::WrongBackendSocket)
+ );
+ }
+
+ #[test]
+ fn test_console_valid_configuration_network_1() {
+ let args = ConsoleArgs {
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Network,
+ tcp_port: String::from("12345"),
+ socket_count: 1,
};
- let result_invalid_count = ConsoleConfiguration::try_from(args_invalid_count);
- assert!(result_invalid_count.is_err());
+ assert!(VuConsoleConfig::try_from(args).is_ok());
}
#[test]
- fn test_start_backend_success() {
- // Test start_backend with valid arguments
+ fn test_console_valid_configuration_network_2() {
let args = ConsoleArgs {
- socket_path: String::from("/path/to/socket"),
- console_path: String::from("vconsole"),
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Network,
+ tcp_port: String::from("12345"),
socket_count: 2,
};
- let result = start_backend(args);
+ assert!(VuConsoleConfig::try_from(args).is_ok());
+ }
+
+ fn test_backend_start_and_stop(args: ConsoleArgs) {
+ let config = VuConsoleConfig::try_from(args).expect("Wrong config");
+
+ let tcp_addrs = config.generate_tcp_addrs();
+ let backend = config.backend;
+
+ for (_socket, tcp_addr) in config
+ .generate_socket_paths()
+ .into_iter()
+ .zip(tcp_addrs.iter())
+ {
+ let controller = ConsoleController::new(backend);
+ let arc_controller = Arc::new(RwLock::new(controller));
+ let vu_console_backend = Arc::new(RwLock::new(
+ VhostUserConsoleBackend::new(arc_controller)
+ .map_err(Error::CouldNotCreateBackend)
+ .expect("Fail create vhuconsole backend"),
+ ));
+
+ let mut _daemon = VhostUserDaemon::new(
+ String::from("vhost-device-console-backend"),
+ vu_console_backend.clone(),
+ GuestMemoryAtomic::new(GuestMemoryMmap::new()),
+ )
+ .map_err(Error::CouldNotCreateDaemon)
+ .expect("Failed create daemon");
+
+ // Start the corresponinding console thread
+ let read_handle = if backend == BackendType::Nested {
+ VhostUserConsoleBackend::start_console_thread(&vu_console_backend)
+ } else {
+ VhostUserConsoleBackend::start_tcp_console_thread(
+ &vu_console_backend,
+ tcp_addr.clone(),
+ )
+ };
+
+ // Kill console input thread
+ vu_console_backend.read().unwrap().kill_console_thread();
+
+ // Wait for read thread to exit
+ assert_matches!(read_handle.join(), Ok(_));
+ }
+ }
+ #[test]
+ fn test_start_net_backend_success() {
+ let args = ConsoleArgs {
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Network,
+ tcp_port: String::from("12345"),
+ socket_count: 1,
+ };
+
+ test_backend_start_and_stop(args);
+ }
+
+ #[test]
+ fn test_start_nested_backend_success() {
+ let args = ConsoleArgs {
+ socket_path: String::from("/tmp/vhost.sock").into(),
+ backend: BackendType::Nested,
+ tcp_port: String::from("12345"),
+ socket_count: 1,
+ };
- assert!(result.is_ok());
+ test_backend_start_and_stop(args);
}
}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs
index 2e2972b2..079fe0cb 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs
@@ -1,86 +1,44 @@
-// CAN backend device
+// Console backend device
//
-// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
-use log::{warn, error};
-use std::sync::{Arc, RwLock};
+use crate::virtio_console::VirtioConsoleConfig;
+use clap::ValueEnum;
+use log::trace;
-use thiserror::Error as ThisError;
-use vm_memory::{ByteValued, Le16};
-
-use crate::vhu_console::{VirtioConsoleConfig, VirtioConsoleControl};
-
-type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
-pub(crate) enum Error {
- #[error("Console not enabled yet")]
- ConsoleNotEnabled,
+#[derive(ValueEnum, Clone, Copy, Default, Debug, Eq, PartialEq)]
+pub enum BackendType {
+ #[default]
+ Nested,
+ Network,
}
#[derive(Debug)]
pub(crate) struct ConsoleController {
config: VirtioConsoleConfig,
- pub console_name: String,
+ pub backend: BackendType,
+ pub exit: bool,
}
impl ConsoleController {
- // Creates a new controller corresponding to `device`.
- pub(crate) fn new(console_name: String) -> Result<ConsoleController> {
-
- let console_name = console_name.to_owned();
- println!("console_name: {:?}", console_name);
-
- Ok(ConsoleController {
+ pub(crate) fn new(backend: BackendType) -> ConsoleController {
+ ConsoleController {
config: VirtioConsoleConfig {
- cols: 20.into(),
- rows: 20.into(),
- max_nr_ports: 1.into(),
- emerg_wr: 64.into(),
- },
- console_name,
- })
+ cols: 20.into(),
+ rows: 20.into(),
+ max_nr_ports: 1.into(),
+ emerg_wr: 64.into(),
+ },
+ backend,
+ exit: false,
+ }
}
pub(crate) fn config(&self) -> &VirtioConsoleConfig {
- log::trace!("Get config\n");
+ trace!("Get config\n");
&self.config
}
-
- pub(crate) fn operation(&self, tx_request: VirtioConsoleControl) -> Result<()> {
- log::trace!("Console operation\n");
- Ok(())
- }
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_new_console_controller() {
- let console_name = String::from("test_console");
- let controller = ConsoleController::new(console_name.clone());
-
- assert!(controller.is_ok());
-
- let controller = controller.unwrap();
- assert_eq!(controller.console_name, "test_console");
- }
-
- #[test]
- fn test_console_controller_config() {
- let console_name = String::from("test_console");
- let controller = ConsoleController::new(console_name).unwrap();
-
- let config = controller.config();
- assert_eq!(config.cols, 20);
- assert_eq!(config.rows, 20);
- assert_eq!(config.max_nr_ports, 1);
- assert_eq!(config.emerg_wr, 64);
- }
-}
-
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs
index cceafb4f..216a01e6 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs
@@ -1,18 +1,69 @@
// VIRTIO CONSOLE Emulation via vhost-user
//
-// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
-#[cfg(target_env = "gnu")]
mod backend;
-#[cfg(target_env = "gnu")]
mod console;
-#[cfg(target_env = "gnu")]
mod vhu_console;
+mod virtio_console;
+use crate::console::BackendType;
+use clap::Parser;
+use log::error;
+use std::path::PathBuf;
+use std::process::exit;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+use crate::backend::{start_backend, Error, VuConsoleConfig};
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct ConsoleArgs {
+ /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
+ #[clap(short = 's', long, value_name = "SOCKET")]
+ socket_path: PathBuf,
+
+ /// Number of guests (sockets) to connect to.
+ #[clap(short = 'c', long, default_value_t = 1)]
+ socket_count: u32,
+
+ /// Console backend (Network, Nested) to be used.
+ #[clap(short = 'b', long, value_enum, default_value = "nested")]
+ backend: BackendType,
+
+ /// Initial tcp port to be used with "network" backend. If socket_count is N then
+ /// the following tcp ports will be created: tcp_port, tcp_port + 1, ..., tcp_port + (N - 1).
+ #[clap(short = 'p', long, value_name = "PORT", default_value = "12345")]
+ tcp_port: String,
+}
+
+impl TryFrom<ConsoleArgs> for VuConsoleConfig {
+ type Error = Error;
+
+ fn try_from(args: ConsoleArgs) -> Result<Self> {
+ if args.socket_count == 0 {
+ return Err(Error::SocketCountInvalid(0));
+ }
+
+ if (args.backend == BackendType::Nested) && (args.socket_count != 1) {
+ return Err(Error::WrongBackendSocket);
+ }
+
+ Ok(VuConsoleConfig {
+ socket_path: args.socket_path,
+ backend: args.backend,
+ tcp_port: args.tcp_port,
+ socket_count: args.socket_count,
+ })
+ }
+}
-#[cfg(target_env = "gnu")]
fn main() {
- backend::console_init()
+ env_logger::init();
+ if let Err(e) = VuConsoleConfig::try_from(ConsoleArgs::parse()).and_then(start_backend) {
+ error!("{e}");
+ exit(1);
+ }
}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs
index ebddb00d..d5cca74a 100644..100755
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs
@@ -1,22 +1,31 @@
// vhost device console
//
-// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
-use log::{warn, error};
-use std::mem::size_of;
+use crate::console::{BackendType, ConsoleController};
+use crate::virtio_console::{
+ VirtioConsoleControl, VIRTIO_CONSOLE_CONSOLE_PORT, VIRTIO_CONSOLE_DEVICE_READY,
+ VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_PORT_ADD, VIRTIO_CONSOLE_PORT_NAME,
+ VIRTIO_CONSOLE_PORT_OPEN, VIRTIO_CONSOLE_PORT_READY,
+};
+use console::Key;
+use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
+use log::{error, trace};
+use nix::sys::select::{select, FdSet};
+use std::os::fd::AsRawFd;
use std::slice::from_raw_parts;
use std::sync::{Arc, RwLock};
+use std::thread::JoinHandle;
use std::{
convert,
io::{self, Result as IoResult},
};
-use std::io::{Write};
-use std::os::fd::AsRawFd;
use thiserror::Error as ThisError;
use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
+use vhost_user_backend::VringEpollHandler;
use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT};
use virtio_bindings::bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1};
use virtio_bindings::bindings::virtio_ring::{
@@ -24,76 +33,61 @@ use virtio_bindings::bindings::virtio_ring::{
};
use virtio_queue::{DescriptorChain, QueueOwnedT};
use vm_memory::{
- ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard,
- GuestMemoryMmap, Le16, Le32,
+ ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap,
};
use vmm_sys_util::epoll::EventSet;
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
-use vhost_user_backend::VringEpollHandler;
-use crate::console::{ConsoleController};
-use std::thread::{JoinHandle, spawn};
-use queues::{Queue, IsQueue};
-use std::thread;
-use std::time::Duration;
use console::Term;
-
-/// Feature bit numbers
-pub const VIRTIO_CONSOLE_F_SIZE: u16 = 0;
-pub const VIRTIO_CONSOLE_F_MULTIPORT: u16 = 1;
-pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u16 = 2;
+use queues::{IsQueue, Queue};
+use std::io::Read;
+use std::io::Write;
+use std::net::TcpListener;
+use std::thread::spawn;
/// Virtio configuration
const QUEUE_SIZE: usize = 128;
const NUM_QUEUES: usize = 4;
-/// Queues
+/// Queue events
const RX_QUEUE: u16 = 0;
const TX_QUEUE: u16 = 1;
const CTRL_RX_QUEUE: u16 = 2;
const CTRL_TX_QUEUE: u16 = 3;
-const BACKEND_EFD: u16 = (NUM_QUEUES + 1) as u16;
-const BACKEND_RX_EFD: u16 = (NUM_QUEUES + 2) as u16;
+/// The two following events are used to help the vhu_console
+/// backend trigger events to itself. For example:
+/// a) BACKEND_RX_EFD is being triggered when the backend
+/// has new data to send to the RX queue.
+/// b) BACKEND_CTRL_RX_EFD event is used when the backend
+/// needs to write to the RX control queue.
+const BACKEND_RX_EFD: u16 = (NUM_QUEUES + 1) as u16;
+const BACKEND_CTRL_RX_EFD: u16 = (NUM_QUEUES + 2) as u16;
-/// Console virtio control messages
-const VIRTIO_CONSOLE_DEVICE_READY: u16 = 0 ;
-const VIRTIO_CONSOLE_PORT_ADD: u16 = 1;
-const VIRTIO_CONSOLE_PORT_REMOVE: u16 = 2;
-const VIRTIO_CONSOLE_PORT_READY: u16 = 3;
-const VIRTIO_CONSOLE_CONSOLE_PORT: u16 = 4;
-const VIRTIO_CONSOLE_RESIZE: u16 = 5;
-const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6;
-const VIRTIO_CONSOLE_PORT_NAME: u16 = 7;
+/// Port name - Need to be updated when MULTIPORT feature
+/// is supported for more than one devices.
+const PORT_NAME: &[u8] = b"org.test.foo!";
type Result<T> = std::result::Result<T, Error>;
#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
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("Failed to send notification")]
- NotificationFailed,
+ #[error("Descriptor not found")]
+ DescriptorNotFound,
+ #[error("Failed to send notification")]
+ NotificationFailed,
#[error("Descriptor read failed")]
DescriptorReadFailed,
#[error("Descriptor write failed")]
DescriptorWriteFailed,
+ #[error("Add used element in vring {0} failed")]
+ AddUsedElemFailed(u16),
#[error("Failed to create new EventFd")]
EventFdFailed,
- #[error("Failed to remove rx queue")]
- EmptyQueue,
+ #[error("Failed to add control message in the internal queue")]
+ RxCtrlQueueAddFailed,
}
impl convert::From<Error> for io::Error {
@@ -102,53 +96,19 @@ impl convert::From<Error> for io::Error {
}
}
-/// Virtio Console Config
-#[derive(Copy, Clone, Debug, Default, PartialEq)]
-#[repr(C)]
-pub(crate) struct VirtioConsoleConfig {
- pub cols: Le16,
- pub rows: Le16,
- pub max_nr_ports: Le32,
- pub emerg_wr: Le32,
-}
-
-// SAFETY: The layout of the structure is fixed and can be initialized by
-// reading its content from byte array.
-unsafe impl ByteValued for VirtioConsoleConfig {}
-
-#[derive(Copy, Clone, Debug, Default, PartialEq)]
-#[repr(C)]
-pub(crate) struct VirtioConsoleControl {
- pub id: Le32,
- pub event: Le16,
- pub value: Le16,
-}
-
-use std::io::Cursor;
-
-impl VirtioConsoleControl {
- fn to_le_bytes(&self) -> Vec<u8> {
- let mut buffer = Vec::new();
-
- buffer.extend_from_slice(&self.id.to_native().to_le_bytes());
- buffer.extend_from_slice(&self.event.to_native().to_le_bytes());
- buffer.extend_from_slice(&self.value.to_native().to_le_bytes());
- buffer
- }
-}
-
// SAFETY: The layout of the structure is fixed and can be initialized by
// reading its content from byte array.
unsafe impl ByteValued for VirtioConsoleControl {}
pub(crate) struct VhostUserConsoleBackend {
controller: Arc<RwLock<ConsoleController>>,
- acked_features: u64,
+ acked_features: u64,
event_idx: bool,
- rx_fifo: Queue<VirtioConsoleControl>,
+ rx_ctrl_fifo: Queue<VirtioConsoleControl>,
+ rx_data_fifo: Queue<String>,
pub(crate) ready: bool,
pub(crate) ready_to_write: bool,
- pub(crate) output_buffer: String,
+ pub(crate) output_queue: Queue<String>,
pub(crate) rx_event: EventFd,
pub(crate) rx_ctrl_event: EventFd,
pub(crate) exit_event: EventFd,
@@ -160,13 +120,14 @@ type ConsoleDescriptorChain = DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMm
impl VhostUserConsoleBackend {
pub(crate) fn new(controller: Arc<RwLock<ConsoleController>>) -> Result<Self> {
Ok(VhostUserConsoleBackend {
- controller: controller,
+ controller,
event_idx: false,
- rx_fifo: Queue::new(),
- acked_features: 0x0,
- ready: false,
- ready_to_write: false,
- output_buffer: String::new(),
+ rx_ctrl_fifo: Queue::new(),
+ rx_data_fifo: Queue::new(),
+ acked_features: 0x0,
+ ready: false,
+ ready_to_write: false,
+ output_queue: Queue::new(),
rx_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
rx_ctrl_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
@@ -174,292 +135,229 @@ impl VhostUserConsoleBackend {
})
}
- fn check_features (&self, features: u16) -> bool {
- (self.acked_features & (1 << features)) != 0
- }
-
- fn print_console_frame (&self, control_msg: VirtioConsoleControl) {
- println!("id 0x{:x}", control_msg.id.to_native());
- println!("event 0x{:x}", control_msg.event.to_native());
- println!("value 0x{:x}", control_msg.value.to_native());
+ fn print_console_frame(&self, control_msg: VirtioConsoleControl) {
+ trace!("id 0x{:x}", control_msg.id.to_native());
+ trace!("event 0x{:x}", control_msg.event.to_native());
+ trace!("value 0x{:x}", control_msg.value.to_native());
}
fn process_rx_requests(
- &mut self,
- requests: Vec<ConsoleDescriptorChain>,
- vring: &VringRwLock
- ) -> Result<bool> {
- log::trace!("process_rx_requests");
-
+ &mut self,
+ requests: Vec<ConsoleDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
if requests.is_empty() {
- log::trace!("requests.is_empty");
- vring.signal_used_queue();
return Ok(true);
}
- log::trace!("requests.len: {:?}", requests.len());
- let desc_chain = &requests[0];
- let descriptors: Vec<_> = desc_chain.clone().collect();
-
- log::trace!("descriptors.len(): {:?}", descriptors.len());
- if descriptors.len() != 1 {
- log::trace!("Error::UnexpectedDescriptorCount");
- return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
- }
-
- let desc_request = descriptors[0];
- if !desc_request.is_write_only() {
- log::trace!("!desc_request.is_write_only()");
- return Err(Error::UnexpectedReadableDescriptor(1));
- }
-
- // TODO: if buffer is more than the the desc_request length,
- // write the remaining in the next chain.
- log::trace!("desc_request.len(): {}", desc_request.len());
- let response = self.output_buffer.clone();
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let response: String = match self.rx_data_fifo.remove() {
+ Ok(item) => item,
+ _ => {
+ return Ok(false);
+ }
+ };
- desc_chain
- .memory()
- .write_slice(response.as_bytes(), desc_request.addr())
- .map_err(|_| Error::DescriptorWriteFailed)?;
+ for b in response.bytes() {
+ writer
+ .write_obj::<u8>(b)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+ }
- if vring.add_used(desc_chain.head_index(), response.as_bytes().len() as u32).is_err() {
- warn!("Couldn't return used descriptors to the ring");
+ vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .map_err(|_| Error::AddUsedElemFailed(RX_QUEUE))?;
}
Ok(true)
}
fn process_tx_requests(
- &self,
- requests: Vec<ConsoleDescriptorChain>,
- vring: &VringRwLock
- ) -> Result<bool> {
- log::trace!("process_tx_requests");
-
+ &mut self,
+ requests: Vec<ConsoleDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
if requests.is_empty() {
- log::trace!("requests.is_empty");
return Ok(true);
}
- log::trace!("requests.len: {:?}", requests.len());
for desc_chain in requests {
- let descriptors: Vec<_> = desc_chain.clone().collect();
-
- log::trace!("descriptors.len(): {:?}", descriptors.len());
- if descriptors.len() != 1 {
- log::trace!("Error::UnexpectedDescriptorCount");
- return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
- }
-
- let desc_request = descriptors[0];
- if desc_request.is_write_only() {
- log::trace!("Error::UnexpectedReadOnlyDescriptor");
- return Err(Error::UnexpectedWriteOnlyDescriptor(0));
- }
-
- log::trace!("desc_request.len(): {}", desc_request.len());
- let desc_len = desc_request.len();
-
- let mut buffer = [0 as u8; 4096];
-
- let request = desc_chain
- .memory()
- .read_slice(&mut buffer, desc_request.addr())
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+ let mut reader = desc_chain
+ .clone()
+ .reader(&atomic_mem)
.map_err(|_| Error::DescriptorReadFailed)?;
- let new_buff = &buffer[0..desc_len as usize];
-
- let my_string = String::from_utf8(new_buff.to_vec()).unwrap();
- log::trace!("{}", my_string);
- print!("{}", my_string);
- io::stdout().flush().unwrap(); // Ensure the prompt is displayed.
+ let mut tx_data: Vec<u8> = Vec::new();
+ let data_len = reader.available_bytes();
+ for _i in 0..data_len {
+ let data_byte = reader
+ .read_obj::<u8>()
+ .map_err(|_| Error::DescriptorReadFailed)?;
+ tx_data.push(data_byte);
+ }
- if vring.add_used(desc_chain.head_index(), desc_request.len()).is_err() {
- log::trace!("Couldn't return used descriptors to the ring");
- warn!("Couldn't return used descriptors to the ring");
+ let my_string = String::from_utf8(tx_data).unwrap();
+ if self.controller.read().unwrap().backend == BackendType::Nested {
+ print!("{}", my_string);
+ io::stdout().flush().unwrap();
+ } else {
+ self.output_queue
+ .add(my_string)
+ .expect("Failed to add element in the output queue");
+ //.map_err(|_| Error::RxCtrlQueueAddFailed)?;
}
+
+ vring
+ .add_used(desc_chain.head_index(), reader.bytes_read() as u32)
+ .map_err(|_| Error::AddUsedElemFailed(TX_QUEUE))?;
}
Ok(true)
}
fn process_ctrl_rx_requests(
- &mut self,
- requests: Vec<ConsoleDescriptorChain>,
- vring: &VringRwLock,
- ) -> Result<bool> {
- log::trace!("process_ctrl_rx_requests");
+ &mut self,
+ requests: Vec<ConsoleDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ let mut used_flag = false;
if requests.is_empty() {
- log::trace!("requests.is_empty()");
return Ok(true);
}
- log::trace!("\trequests.len(): {}", requests.len());
for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+ let mut writer = desc_chain
+ .clone()
+ .writer(&atomic_mem)
+ .map_err(|_| Error::DescriptorWriteFailed)?;
+
+ let ctrl_msg: VirtioConsoleControl = match self.rx_ctrl_fifo.remove() {
+ Ok(item) => {
+ used_flag = true;
+ item
+ }
+ _ => {
+ return Ok(used_flag);
+ }
+ };
- let descriptors: Vec<_> = desc_chain.clone().collect();
+ self.print_console_frame(ctrl_msg);
- log::trace!("descriptors.len(): {:?}", descriptors.len());
- if descriptors.len() < 1 {
- warn!("Error::UnexpectedDescriptorCount");
- return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
- }
+ let mut buffer: Vec<u8> = Vec::new();
+ buffer.extend_from_slice(&ctrl_msg.to_le_bytes());
- log::trace!("self.rx_fifo.size(): {}", self.rx_fifo.size());
- let ctrl_msg: VirtioConsoleControl = match self.rx_fifo.remove() {
- Ok(item) => item,
- _ => {
- log::trace!("No rx elements");
- return Ok(false)
- },
- };
-
- let desc_request = descriptors[0];
- if !desc_request.is_write_only() {
- warn!("Error::UnexpectedWriteOnlyDescriptor");
- return Err(Error::UnexpectedWriteOnlyDescriptor(0));
- }
+ if ctrl_msg.event.to_native() == VIRTIO_CONSOLE_PORT_NAME {
+ buffer.extend_from_slice(PORT_NAME);
+ };
- if (desc_request.len() as usize) < size_of::<VirtioConsoleControl>() {
- log::trace!("UnexpectedDescriptorSize, len = {:?}", desc_request.len());
- return Err(Error::UnexpectedDescriptorSize(
- size_of::<VirtioConsoleControl>(),
- desc_request.len(),
- ));
- }
+ writer
+ .write(buffer.as_slice())
+ .map_err(|_| Error::DescriptorWriteFailed)?;
- log::trace!("desc_request.len(): {}", desc_request.len());
- self.print_console_frame(ctrl_msg);
-
- let mut buffer: Vec<u8> = Vec::new();
- buffer.extend_from_slice(&ctrl_msg.to_le_bytes());
-
- if ctrl_msg.event.to_native() == VIRTIO_CONSOLE_PORT_NAME {
- let string_bytes = "org.fedoraproject.console.foo!".as_bytes();
- buffer.extend_from_slice(string_bytes);
- };
-
- desc_chain
- .memory()
- .write_slice(&buffer, desc_request.addr())
- .map_err(|_| Error::DescriptorWriteFailed)?;
-
- if vring.add_used(desc_chain.head_index(), desc_request.len()).is_err() {
- log::trace!("Couldn't return used descriptors to the ring");
- warn!("Couldn't return used descriptors to the ring");
- }
+ vring
+ .add_used(desc_chain.head_index(), writer.bytes_written() as u32)
+ .map_err(|_| Error::AddUsedElemFailed(CTRL_RX_QUEUE))?;
}
Ok(true)
}
- fn handle_control_msg (
- &mut self,
- vring: &VringRwLock,
- ctrl_msg: VirtioConsoleControl
- ) -> Result<()> {
-
- let mut ctrl_msg_reply = VirtioConsoleControl {
- id: 0.into(),
- event: 0.into(),
- value: 1.into(),
- };
- match ctrl_msg.event.to_native() {
- VIRTIO_CONSOLE_DEVICE_READY => {
- log::trace!("VIRTIO_CONSOLE_DEVICE_READY");
- self.ready = true;
- ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_ADD.into();
- self.rx_fifo.add(ctrl_msg_reply);
- self.process_ctrl_rx_queue(vring)?;
- },
- VIRTIO_CONSOLE_PORT_READY => {
- log::trace!("VIRTIO_CONSOLE_PORT_READY");
- ctrl_msg_reply.event = VIRTIO_CONSOLE_CONSOLE_PORT.into();
- self.rx_fifo.add(ctrl_msg_reply.clone());
- self.process_ctrl_rx_queue(vring)?;
-
- ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_NAME.into();
- self.rx_fifo.add(ctrl_msg_reply.clone());
- self.process_ctrl_rx_queue(vring)?;
-
- ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_OPEN.into();
- self.rx_fifo.add(ctrl_msg_reply.clone());
- self.process_ctrl_rx_queue(vring)?;
- },
- VIRTIO_CONSOLE_PORT_OPEN => {
- log::trace!("VIRTIO_CONSOLE_PORT_OPEN");
- },
- _ => {
- log::trace!("Uknown control event");
- return Err(Error::HandleEventUnknown);
- }
- };
- Ok(())
- }
+ fn handle_control_msg(&mut self, ctrl_msg: VirtioConsoleControl) -> Result<()> {
+ let mut ctrl_msg_reply = VirtioConsoleControl {
+ id: 0.into(),
+ event: 0.into(),
+ value: 1.into(),
+ };
+ match ctrl_msg.event.to_native() {
+ VIRTIO_CONSOLE_DEVICE_READY => {
+ trace!("VIRTIO_CONSOLE_DEVICE_READY");
- fn process_ctrl_tx_requests(
- &mut self,
- requests: Vec<ConsoleDescriptorChain>,
- vring: &VringRwLock,
- rx_ctrl_vring: &VringRwLock
- ) -> Result<bool> {
- log::trace!("process_ctrl_tx_requests");
+ if ctrl_msg.value != 1 {
+ trace!("Guest failure in adding device");
+ return Ok(());
+ }
- if requests.is_empty() {
- log::trace!("requests.is_empty()");
- return Ok(true);
- }
+ self.ready = true;
+ ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_ADD.into();
+ self.rx_ctrl_fifo
+ .add(ctrl_msg_reply)
+ .map_err(|_| Error::RxCtrlQueueAddFailed)?;
+ }
+ VIRTIO_CONSOLE_PORT_READY => {
+ trace!("VIRTIO_CONSOLE_PORT_READY");
- for desc_chain in requests {
- let descriptors: Vec<_> = desc_chain.clone().collect();
+ if ctrl_msg.value != 1 {
+ trace!("Guest failure in adding port for device");
+ return Ok(());
+ }
- if descriptors.len() < 1 {
- warn!("Error::UnexpectedDescriptorCount");
- return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
- }
+ ctrl_msg_reply.event = VIRTIO_CONSOLE_CONSOLE_PORT.into();
+ self.rx_ctrl_fifo
+ .add(ctrl_msg_reply)
+ .map_err(|_| Error::RxCtrlQueueAddFailed)?;
- log::trace!("descriptors.len(): {:?}", descriptors.len());
+ ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_NAME.into();
+ self.rx_ctrl_fifo
+ .add(ctrl_msg_reply)
+ .map_err(|_| Error::RxCtrlQueueAddFailed)?;
- let desc_request = descriptors[0];
- if desc_request.is_write_only() {
- log::trace!("This is write only");
- return Err(Error::UnexpectedWriteOnlyDescriptor(0));
- } else {
- log::trace!("This is read only");
- }
-
- if desc_request.len() as usize != size_of::<VirtioConsoleControl>() {
- log::trace!("UnexpectedDescriptorSize, len = {:?}", desc_request.len());
- return Err(Error::UnexpectedDescriptorSize(
- size_of::<VirtioConsoleControl>(),
- desc_request.len(),
- ));
+ ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_OPEN.into();
+ self.rx_ctrl_fifo
+ .add(ctrl_msg_reply)
+ .map_err(|_| Error::RxCtrlQueueAddFailed)?;
+ }
+ VIRTIO_CONSOLE_PORT_OPEN => {
+ trace!("VIRTIO_CONSOLE_PORT_OPEN");
+ }
+ _ => {
+ trace!("Uknown control event");
+ return Err(Error::HandleEventUnknown);
}
+ };
+ Ok(())
+ }
+
+ fn process_ctrl_tx_requests(
+ &mut self,
+ requests: Vec<ConsoleDescriptorChain>,
+ vring: &VringRwLock,
+ ) -> Result<bool> {
+ if requests.is_empty() {
+ return Ok(true);
+ }
- log::trace!("desc_request.len: {}", desc_request.len());
+ for desc_chain in requests {
+ let atomic_mem = self.mem.as_ref().unwrap().memory();
+ let mut reader = desc_chain
+ .clone()
+ .reader(&atomic_mem)
+ .map_err(|_| Error::DescriptorReadFailed)?;
- let mut request = desc_chain
- .memory()
- .read_obj::<VirtioConsoleControl>(desc_request.addr())
+ let request = reader
+ .read_obj::<VirtioConsoleControl>()
.map_err(|_| Error::DescriptorReadFailed)?;
- self.print_console_frame(request);
+ // Print the receive console frame
+ self.print_console_frame(request);
- self.handle_control_msg(rx_ctrl_vring, request);
+ // Process the received control frame
+ self.handle_control_msg(request)?;
- if let Some(event_fd) = rx_ctrl_vring.get_ref().get_kick() {
- self.rx_ctrl_event.write(1).unwrap();
- } else {
- // Handle the case where `state` is `None`.
- log::trace!("EventFd is not available.");
- }
+ // trigger a kick to the CTRL_RT_QUEUE
+ self.rx_ctrl_event.write(1).unwrap();
- if vring.add_used(desc_chain.head_index(), desc_request.len()).is_err() {
- log::trace!("Couldn't return used descriptors to the ring");
- warn!("Couldn't return used descriptors to the ring");
- }
+ vring
+ .add_used(desc_chain.head_index(), reader.bytes_read() as u32)
+ .map_err(|_| Error::AddUsedElemFailed(CTRL_TX_QUEUE))?;
}
Ok(true)
@@ -467,7 +365,6 @@ impl VhostUserConsoleBackend {
/// Process the messages in the vring and dispatch replies
fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
- log::trace!("process_rx_queue");
let requests: Vec<_> = vring
.get_mut()
.get_queue_mut()
@@ -477,20 +374,15 @@ impl VhostUserConsoleBackend {
if self.process_rx_requests(requests, vring)? {
// Send notification once all the requests are processed
- log::trace!("Send notification once all the requests of queue 0 are processed");
vring
.signal_used_queue()
- .map_err(|_| {
- log::trace!("NotificationFailed");
- Error::NotificationFailed
- })?;
+ .map_err(|_| Error::NotificationFailed)?;
}
- Ok(())
+ Ok(())
}
/// Process the messages in the vring and dispatch replies
- fn process_tx_queue(&self, vring: &VringRwLock) -> Result<()> {
- log::trace!("process_tx_queue");
+ fn process_tx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
let requests: Vec<_> = vring
.get_mut()
.get_queue_mut()
@@ -500,21 +392,15 @@ impl VhostUserConsoleBackend {
if self.process_tx_requests(requests, vring)? {
// Send notification once all the requests are processed
- log::trace!("Send notification once all the requests of queue 1 are processed");
vring
.signal_used_queue()
- .map_err(|_| {
- log::trace!("signal_used_queue error");
- Error::NotificationFailed
- })?;
+ .map_err(|_| Error::NotificationFailed)?;
}
-
Ok(())
}
/// Process the messages in the vring and dispatch replies
fn process_ctrl_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
- log::trace!("process_ctrl_rx_queue");
let requests: Vec<_> = vring
.get_mut()
.get_queue_mut()
@@ -523,18 +409,16 @@ impl VhostUserConsoleBackend {
.collect();
if self.process_ctrl_rx_requests(requests, vring)? {
- log::trace!("Send notification once all the requests of queue 2 are processed");
// Send notification once all the requests are processed
vring
.signal_used_queue()
.map_err(|_| Error::NotificationFailed)?;
}
- Ok(())
+ Ok(())
}
/// Process the messages in the vring and dispatch replies
- fn process_ctrl_tx_queue(&mut self, vring: &VringRwLock, rx_ctrl_vring: &VringRwLock) -> Result<()> {
- log::trace!("process_ctrl_tx_queue");
+ fn process_ctrl_tx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
let requests: Vec<_> = vring
.get_mut()
.get_queue_mut()
@@ -542,113 +426,257 @@ impl VhostUserConsoleBackend {
.map_err(|_| Error::DescriptorNotFound)?
.collect();
- if self.process_ctrl_tx_requests(requests, vring, rx_ctrl_vring)? {
+ if self.process_ctrl_tx_requests(requests, vring)? {
// Send notification once all the requests are processed
vring
.signal_used_queue()
.map_err(|_| Error::NotificationFailed)?;
}
- Ok(())
+ Ok(())
}
/// Set self's VringWorker.
pub(crate) fn set_vring_worker(
- &self,
- vring_worker: &Arc<VringEpollHandler<Arc<RwLock<VhostUserConsoleBackend>>, VringRwLock, ()>>,
+ &self,
+ vring_worker: &Arc<VringEpollHandler<Arc<RwLock<VhostUserConsoleBackend>>>>,
) {
- let rx_event_fd = self.rx_event.as_raw_fd();
- vring_worker
- .register_listener(
- rx_event_fd,
- EventSet::IN,
- u64::from(BACKEND_EFD))
+ let rx_event_fd = self.rx_event.as_raw_fd();
+ vring_worker
+ .register_listener(rx_event_fd, EventSet::IN, u64::from(BACKEND_RX_EFD))
.unwrap();
- let rx_ctrl_event_fd = self.rx_ctrl_event.as_raw_fd();
- vring_worker
+ let rx_ctrl_event_fd = self.rx_ctrl_event.as_raw_fd();
+ vring_worker
.register_listener(
- rx_ctrl_event_fd,
- EventSet::IN,
- u64::from(BACKEND_RX_EFD))
+ rx_ctrl_event_fd,
+ EventSet::IN,
+ u64::from(BACKEND_CTRL_RX_EFD),
+ )
.unwrap();
}
+ pub(crate) fn start_tcp_console_thread(
+ vhu_console: &Arc<RwLock<VhostUserConsoleBackend>>,
+ tcplisener_str: String,
+ ) -> JoinHandle<Result<()>> {
+ let vhu_console = Arc::clone(vhu_console);
+ spawn(move || {
+ loop {
+ let ready = vhu_console.read().unwrap().ready_to_write;
+ let exit = vhu_console.read().unwrap().controller.read().unwrap().exit;
+
+ if exit {
+ trace!("Thread exits!");
+ break;
+ } else if ready {
+ let listener = match TcpListener::bind(tcplisener_str.clone()) {
+ Ok(listener) => listener,
+ Err(e) => {
+ eprintln!("Failed to bind to {}: {}", tcplisener_str, e);
+ return Ok(());
+ }
+ };
+ listener.set_nonblocking(true).expect("Non-blocking error");
+
+ println!("Server listening on address: {}", tcplisener_str.clone());
+ for stream in listener.incoming() {
+ match stream {
+ Ok(mut stream) => {
+ trace!("New connection");
+ stream.set_nonblocking(true).expect("Non-blocking error");
+
+ let mut buffer = [0; 1024];
+ loop {
+ let exit =
+ vhu_console.read().unwrap().controller.read().unwrap().exit;
+ if exit {
+ trace!("Thread exits!");
+ return Ok(());
+ }
+ // Write to the stream
+ if vhu_console.read().unwrap().output_queue.size() > 0 {
+ let byte_stream = vhu_console
+ .write()
+ .unwrap()
+ .output_queue
+ .remove()
+ .expect("Error removing element from output queue")
+ .into_bytes();
+ if let Err(e) = stream.write_all(&byte_stream) {
+ eprintln!("Error writing to stream: {}", e);
+ }
+ }
+ match stream.read(&mut buffer) {
+ Ok(bytes_read) => {
+ if bytes_read == 0 {
+ println!("Close connection");
+ break;
+ }
+ trace!(
+ "Received: {}",
+ String::from_utf8_lossy(&buffer[..bytes_read])
+ );
+ let input_buffer =
+ String::from_utf8_lossy(&buffer[..bytes_read])
+ .to_string();
+ vhu_console
+ .write()
+ .unwrap()
+ .rx_data_fifo
+ .add(input_buffer)
+ .unwrap();
+ vhu_console.write().unwrap().rx_event.write(1).unwrap();
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ continue;
+ }
+ Err(ref e)
+ if e.kind() == io::ErrorKind::BrokenPipe
+ || e.kind() == io::ErrorKind::ConnectionReset =>
+ {
+ trace!("Stream has been closed.");
+ break;
+ }
+ Err(e) => {
+ eprintln!("Error reading from socket: {}", e);
+ }
+ }
+ }
+ }
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ let exit =
+ vhu_console.read().unwrap().controller.read().unwrap().exit;
+ if exit {
+ trace!("Thread exits!");
+ return Ok(());
+ }
+ continue;
+ }
+ Err(e) => {
+ eprintln!("Error accepting connection: {}", e);
+ break;
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+ })
+ }
+
/// Start console thread.
pub(crate) fn start_console_thread(
- vhu_console: &Arc<RwLock<VhostUserConsoleBackend>>,
- ) {
+ vhu_console: &Arc<RwLock<VhostUserConsoleBackend>>,
+ ) -> JoinHandle<Result<()>> {
+ let vhu_console = Arc::clone(vhu_console);
+
+ let exit_eventfd = vhu_console.read().unwrap().exit_event.as_raw_fd();
+ // Spawn a new thread to handle input.
+ spawn(move || {
+ let term = Term::stdout();
+ let mut fdset = FdSet::new();
+ fdset.insert(term.as_raw_fd());
+ fdset.insert(exit_eventfd);
+ let max_fd = fdset.highest().expect("Failed to read fdset!") + 1;
- let vhu_console = Arc::clone(&vhu_console);
- print!("Enter text and press Enter: ");
-
- // Spawn a new thread to handle input.
- spawn( move || {
- loop {
+ loop {
let ready = vhu_console.read().unwrap().ready_to_write;
- if ready {
-
- let term = Term::stdout();
- let character = term.read_char().unwrap();
- log::trace!("You entered: {}", character);
+ let exit = vhu_console.read().unwrap().controller.read().unwrap().exit;
- // Pass the data to vhu_console and trigger an EventFd
- vhu_console.write().unwrap().output_buffer = character.to_string();
- vhu_console.write().unwrap().rx_event.write(1).unwrap();
+ if exit {
+ trace!("Exit!");
+ break;
+ } else if ready {
+ let mut fdset_clone = fdset;
+ enable_raw_mode().expect("Raw mode error");
+
+ match select(Some(max_fd), Some(&mut fdset_clone), None, None, None) {
+ Ok(_num_fds) => {
+ let exit = vhu_console.read().unwrap().controller.read().unwrap().exit;
+ if (fdset_clone.contains(exit_eventfd)) && exit {
+ trace!("Exit!");
+ break;
+ }
+
+ if fdset_clone.contains(term.as_raw_fd()) {
+ if let Some(character) = match term.read_key().unwrap() {
+ Key::Char(character) => Some(character),
+ Key::Enter => Some('\n'),
+ Key::Tab => Some('\t'),
+ Key::Backspace => Some('\u{8}'),
+ _ => None,
+ } {
+ // Pass the data to vhu_console and trigger an EventFd
+ let input_buffer = character.to_string();
+ vhu_console
+ .write()
+ .unwrap()
+ .rx_data_fifo
+ .add(input_buffer)
+ .unwrap();
+ vhu_console.write().unwrap().rx_event.write(1).unwrap();
+ }
+ }
+ }
+ Err(e) => {
+ eprintln!("Error in select: {}", e);
+ break;
+ }
+ }
}
- }
- });
+ }
+
+ disable_raw_mode().expect("Raw mode error");
+ Ok(())
+ })
+ }
+ pub fn kill_console_thread(&self) {
+ trace!("Kill thread");
+ self.controller.write().unwrap().exit = true;
+ self.exit_event.write(1).unwrap();
}
}
/// VhostUserBackendMut trait methods
-impl VhostUserBackendMut<VringRwLock, ()>
- for VhostUserConsoleBackend
-{
+impl VhostUserBackendMut for VhostUserConsoleBackend {
+ type Vring = VringRwLock;
+ type Bitmap = ();
+
fn num_queues(&self) -> usize {
- log::trace!("num_queues: {:?}", NUM_QUEUES);
NUM_QUEUES
}
fn max_queue_size(&self) -> usize {
- log::trace!("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_VERSION_1
| 1 << VIRTIO_F_NOTIFY_ON_EMPTY
| 1 << VIRTIO_RING_F_EVENT_IDX
- | 1 << VIRTIO_CONSOLE_F_EMERG_WRITE
| 1 << VIRTIO_RING_F_INDIRECT_DESC
- | 1 << VIRTIO_CONSOLE_F_MULTIPORT // This could be disabled
- | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
-
- log::trace!("vhu_can->features: {:x}", features);
- features
+ | 1 << VIRTIO_CONSOLE_F_MULTIPORT
+ | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
}
- fn acked_features(&mut self, _features: u64) {
- log::trace!("\nacked_features: 0x{:x}\n", _features);
- self.acked_features = _features;
- }
+ fn acked_features(&mut self, features: u64) {
+ self.acked_features = features;
+ }
fn protocol_features(&self) -> VhostUserProtocolFeatures {
- let protocol_features = VhostUserProtocolFeatures::MQ
+ VhostUserProtocolFeatures::MQ
| VhostUserProtocolFeatures::CONFIG
- | VhostUserProtocolFeatures::REPLY_ACK;
-
- log::trace!("protocol_features: {:x}", protocol_features);
- protocol_features
+ | VhostUserProtocolFeatures::REPLY_ACK
}
fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
// SAFETY: The layout of the structure is fixed and can be initialized by
// reading its content from byte array.
- log::trace!("vhu_can->get_config");
unsafe {
from_raw_parts(
- self.controller.write().unwrap()
+ self.controller
+ .write()
+ .unwrap()
.config()
.as_slice()
.as_ptr()
@@ -660,11 +688,10 @@ impl VhostUserBackendMut<VringRwLock, ()>
}
fn set_event_idx(&mut self, enabled: bool) {
- dbg!(self.event_idx = enabled);
+ self.event_idx = enabled;
}
fn update_memory(&mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>) -> IoResult<()> {
- log::trace!("update_memory\n");
self.mem = Some(mem);
Ok(())
}
@@ -672,93 +699,81 @@ impl VhostUserBackendMut<VringRwLock, ()>
fn handle_event(
&mut self,
device_event: u16,
- evset: EventSet,
+ _evset: EventSet,
vrings: &[VringRwLock],
_thread_id: usize,
- ) -> IoResult<bool> {
- log::trace!("\nhandle_event:");
-
- if device_event == RX_QUEUE {
- log::trace!("RX_QUEUE\n");
- return Ok(false);
- };
-
- if device_event == CTRL_RX_QUEUE {
- log::trace!("CTRL_RX_QUEUE\n");
- if !self.ready {
- return Ok(false);
- }
- };
-
- let vring = if device_event == BACKEND_EFD {
- log::trace!("BACKEND_EFD\n");
- &vrings[RX_QUEUE as usize]
- } else if device_event == BACKEND_RX_EFD {
- log::trace!("BACKEND_RX_EFD\n");
- &vrings[CTRL_RX_QUEUE as usize]
- } else {
- &vrings[device_event as usize]
- };
+ ) -> IoResult<()> {
+ if device_event == RX_QUEUE {
+ // Check if there are any available data
+ if self.rx_data_fifo.size() == 0 {
+ return Ok(());
+ }
+ };
+
+ if device_event == CTRL_RX_QUEUE {
+ // Check if there are any available data and the device is ready
+ if (!self.ready) || (self.rx_ctrl_fifo.size() == 0) {
+ return Ok(());
+ }
+ };
+
+ let vring = if device_event == BACKEND_RX_EFD {
+ &vrings[RX_QUEUE as usize]
+ } else if device_event == BACKEND_CTRL_RX_EFD {
+ &vrings[CTRL_RX_QUEUE as usize]
+ } else {
+ &vrings[device_event as usize]
+ };
if self.event_idx {
- // vm-virtio's Queue implementation only checks avail_index
- // once, so to properly support EVENT_IDX we need to keep
- // calling process_request_queue() until it stops finding
- // new requests on the queue.
loop {
vring.disable_notification().unwrap();
match device_event {
+ RX_QUEUE => self.process_rx_queue(vring),
TX_QUEUE => {
- self.ready_to_write = true;
+ self.ready_to_write = true;
self.process_tx_queue(vring)
- },
+ }
CTRL_RX_QUEUE => self.process_ctrl_rx_queue(vring),
- CTRL_TX_QUEUE => {
- let rx_ctrl_vring = &vrings[CTRL_RX_QUEUE as usize];
- self.process_ctrl_tx_queue(vring, rx_ctrl_vring)
- },
- BACKEND_EFD => {
- let _ = self.rx_event.read();
- self.process_rx_queue(vring)
- },
+ CTRL_TX_QUEUE => self.process_ctrl_tx_queue(vring),
BACKEND_RX_EFD => {
- let _ = self.rx_ctrl_event.read();
- self.process_ctrl_rx_queue(vring)
- },
- _ => Err(Error::HandleEventUnknown.into()),
+ let _ = self.rx_event.read();
+ self.process_rx_queue(vring)
+ }
+ BACKEND_CTRL_RX_EFD => {
+ let _ = self.rx_ctrl_event.read();
+ self.process_ctrl_rx_queue(vring)
+ }
+ _ => Err(Error::HandleEventUnknown),
}?;
if !vring.enable_notification().unwrap() {
break;
}
}
- } else {
- // Without EVENT_IDX, a single call is enough.
+ } else {
match device_event {
+ RX_QUEUE => self.process_rx_queue(vring),
TX_QUEUE => {
- self.ready_to_write = true;
+ self.ready_to_write = true;
self.process_tx_queue(vring)
- },
+ }
CTRL_RX_QUEUE => self.process_ctrl_rx_queue(vring),
- CTRL_TX_QUEUE => {
- let rx_ctrl_vring = &vrings[CTRL_RX_QUEUE as usize];
- self.process_ctrl_tx_queue(vring, rx_ctrl_vring)
- },
- BACKEND_EFD => {
- let _ = self.rx_event.read();
- self.process_rx_queue(vring)
- },
+ CTRL_TX_QUEUE => self.process_ctrl_tx_queue(vring),
BACKEND_RX_EFD => {
- let _ = self.rx_ctrl_event.read();
- self.process_ctrl_rx_queue(vring)
- },
- _ => Err(Error::HandleEventUnknown.into()),
+ let _ = self.rx_event.read();
+ self.process_rx_queue(vring)
+ }
+ BACKEND_CTRL_RX_EFD => {
+ let _ = self.rx_ctrl_event.read();
+ self.process_ctrl_rx_queue(vring)
+ }
+ _ => Err(Error::HandleEventUnknown),
}?;
}
- Ok(false)
+ Ok(())
}
fn exit_event(&self, _thread_index: usize) -> Option<EventFd> {
- dbg!("exit_event\n");
self.exit_event.try_clone().ok()
}
}
@@ -766,29 +781,503 @@ impl VhostUserBackendMut<VringRwLock, ()>
#[cfg(test)]
mod tests {
use super::*;
+ use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE};
+ use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue};
+ use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
+
+ #[test]
+ fn test_vhost_user_console_backend_creation() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let vhost_user_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ assert_eq!(vhost_user_console_backend.acked_features, 0);
+ assert!(!vhost_user_console_backend.event_idx);
+ assert!(!vhost_user_console_backend.ready);
+ assert!(!vhost_user_console_backend.ready_to_write);
+ }
+
+ #[test]
+ fn test_virtio_console_empty_handle_request() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryAtomic::new(
+ GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
+ );
+
+ // Update memory
+ vu_console_backend.update_memory(mem.clone()).unwrap();
+
+ // Artificial Vring
+ let vring = VringRwLock::new(mem, 0x1000).unwrap();
+ vring.set_queue_info(0x100, 0x200, 0x300).unwrap();
+ vring.set_queue_ready(true);
+ let list_vrings = [vring.clone(), vring.clone(), vring.clone(), vring.clone()];
+
+ vu_console_backend
+ .handle_event(RX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_console_backend
+ .handle_event(TX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_console_backend
+ .handle_event(CTRL_RX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_console_backend
+ .handle_event(CTRL_TX_QUEUE, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_console_backend
+ .handle_event(BACKEND_RX_EFD, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+
+ vu_console_backend
+ .handle_event(BACKEND_CTRL_RX_EFD, EventSet::IN, &list_vrings, 0)
+ .unwrap();
+ }
+
+ #[test]
+ fn test_virtio_console_empty_requests() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryAtomic::new(
+ GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
+ );
+
+ // Artificial Vring
+ let vring = VringRwLock::new(mem.clone(), 0x1000).unwrap();
+
+ // Empty descriptor chain should be ignored
+ assert!(vu_console_backend
+ .process_rx_requests(Vec::<ConsoleDescriptorChain>::new(), &vring)
+ .is_ok());
+ assert!(vu_console_backend
+ .process_tx_requests(Vec::<ConsoleDescriptorChain>::new(), &vring)
+ .is_ok());
+ assert!(vu_console_backend
+ .process_ctrl_rx_requests(Vec::<ConsoleDescriptorChain>::new(), &vring)
+ .is_ok());
+ assert!(vu_console_backend
+ .process_ctrl_tx_requests(Vec::<ConsoleDescriptorChain>::new(), &vring)
+ .is_ok());
+ }
+
+ fn build_desc_chain(
+ mem: &GuestMemoryMmap,
+ count: u16,
+ flags: Vec<u16>,
+ len: u32,
+ ) -> ConsoleDescriptorChain {
+ let vq = MockSplitQueue::new(mem, 16);
+ let mut desc_vec = Vec::new();
+
+ //Create a descriptor chain with @count descriptors.
+ for i in 0..count {
+ let desc_flags = if i < count - 1 {
+ flags[i as usize] | VRING_DESC_F_NEXT as u16
+ } else {
+ flags[i as usize] & !VRING_DESC_F_NEXT as u16
+ };
+
+ let desc = Descriptor::new((0x100 * (i + 1)) as u64, len, desc_flags, i + 1);
+ desc_vec.push(desc);
+ }
+
+ vq.add_desc_chains(&desc_vec, 0).unwrap();
+
+ // Create descriptor chain from pre-filled memory
+ vq.create_queue::<Queue>()
+ .unwrap()
+ .iter(GuestMemoryAtomic::new(mem.clone()).memory())
+ .unwrap()
+ .next()
+ .unwrap()
+ }
#[test]
- fn test_virtio_console_control_byte_valued() {
- let control = VirtioConsoleControl {
- id: Le32::from(1),
- event: Le16::from(2),
- value: Le16::from(3),
+ fn test_virtio_console_ctrl_rx_request() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Test 1: Empty queue
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ assert!(vu_console_backend
+ .process_ctrl_rx_requests(vec![], &vring)
+ .unwrap());
+
+ // Test 2: Found no rx elements
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+
+ assert!(!vu_console_backend
+ .process_ctrl_rx_requests(vec![desc_chain], &vring)
+ .unwrap());
+
+ // Test 3: empty queue
+ let desc_chain = build_desc_chain(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+ assert!(!vu_console_backend
+ .process_ctrl_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ // Test 4: the written desc reply
+ let ctrl_msg = VirtioConsoleControl {
+ id: 0.into(),
+ event: VIRTIO_CONSOLE_PORT_ADD.into(),
+ value: 1.into(),
};
+ let _ = vu_console_backend.rx_ctrl_fifo.add(ctrl_msg);
- let bytes = control.to_le_bytes();
+ assert!(vu_console_backend
+ .process_ctrl_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
- assert_eq!(bytes.len(), 10);
+ let ctrl_msg_reply = desc_chain
+ .memory()
+ .read_obj::<VirtioConsoleControl>(vm_memory::GuestAddress(0x100_u64))
+ .map_err(|_| Error::DescriptorReadFailed)
+ .unwrap();
+
+ assert_eq!(ctrl_msg.id, ctrl_msg_reply.id);
+ assert_eq!(ctrl_msg.event, ctrl_msg_reply.event);
+ assert_eq!(ctrl_msg.value, ctrl_msg_reply.value);
+
+ // Test 5: if message is VIRTIO_CONSOLE_PORT_NAME
+ let desc_chain = build_desc_chain(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+
+ let ctrl_msg = VirtioConsoleControl {
+ id: 0.into(),
+ event: VIRTIO_CONSOLE_PORT_NAME.into(),
+ value: 1.into(),
+ };
+ let _ = vu_console_backend.rx_ctrl_fifo.add(ctrl_msg);
+
+ assert!(vu_console_backend
+ .process_ctrl_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
}
#[test]
- fn test_vhost_user_console_backend_creation() {
- let console_controller = Arc::new(RwLock::new(ConsoleController::new(String::from("test_console")).unwrap()));
- let vhost_user_console_backend = VhostUserConsoleBackend::new(console_controller).unwrap();
+ fn test_virtio_console_ctrl_tx_request() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Test 1: Empty queue
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ assert!(vu_console_backend
+ .process_ctrl_tx_requests(vec![], &vring)
+ .unwrap());
+
+ // Test 2: Found no descriptors
+ let desc_chain = build_desc_chain(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+
+ assert_eq!(
+ vu_console_backend
+ .process_ctrl_tx_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 3: Smaller descriptor len than a console ctrl message
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x2);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+ assert_eq!(
+ vu_console_backend
+ .process_ctrl_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap_err(),
+ Error::DescriptorReadFailed
+ );
+
+ // Test 4: Complete function successfully -- VIRTIO_CONSOLE_PORT_READY message
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x8);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+ assert!(vu_console_backend
+ .process_ctrl_tx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+ }
- assert_eq!(vhost_user_console_backend.acked_features, 0);
- assert_eq!(vhost_user_console_backend.event_idx, false);
- assert_eq!(vhost_user_console_backend.ready, false);
- assert_eq!(vhost_user_console_backend.ready_to_write, false);
- assert_eq!(vhost_user_console_backend.output_buffer, String::new());
+ #[test]
+ fn test_virtio_console_handle_control_msg() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory & update device's memory
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+ let mem_1 = GuestMemoryAtomic::new(mem.clone());
+ vu_console_backend.update_memory(mem_1.clone()).unwrap();
+
+ // Test 1: Empty queue
+ let ctrl_msg_1 = VirtioConsoleControl {
+ id: 0.into(),
+ event: VIRTIO_CONSOLE_DEVICE_READY.into(),
+ value: 1.into(),
+ };
+
+ let ctrl_msg_2 = VirtioConsoleControl {
+ id: 0.into(),
+ event: VIRTIO_CONSOLE_PORT_READY.into(),
+ value: 1.into(),
+ };
+
+ let ctrl_msg_3 = VirtioConsoleControl {
+ id: 0.into(),
+ event: VIRTIO_CONSOLE_PORT_OPEN.into(),
+ value: 1.into(),
+ };
+
+ let ctrl_msg_err = VirtioConsoleControl {
+ id: 0.into(),
+ event: 4.into(),
+ value: 1.into(),
+ };
+
+ assert!(vu_console_backend.handle_control_msg(ctrl_msg_3).is_ok());
+
+ assert_eq!(
+ vu_console_backend
+ .handle_control_msg(ctrl_msg_err)
+ .unwrap_err(),
+ Error::HandleEventUnknown
+ );
+
+ assert!(vu_console_backend.handle_control_msg(ctrl_msg_1).is_ok());
+
+ // Update memory
+ let mem_1 = GuestMemoryAtomic::new(mem.clone());
+ vu_console_backend.update_memory(mem_1.clone()).unwrap();
+
+ assert!(vu_console_backend.handle_control_msg(ctrl_msg_2).is_ok());
+ }
+
+ #[test]
+ fn test_virtio_console_tx_request() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Test 1: Empty queue
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1, 0x1000).unwrap();
+ assert!(vu_console_backend
+ .process_tx_requests(vec![], &vring)
+ .is_ok());
+
+ // Test 2: Empty buffer
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+ assert!(vu_console_backend
+ .process_tx_requests(vec![desc_chain], &vring)
+ .is_ok());
+
+ // Test 3: Fill message to the buffer
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
+ let desc_addr = desc_chain.clone().collect::<Vec<_>>()[0].addr();
+
+ // Build the Vec with the desired string
+ let mut buffer: Vec<u8> = Vec::new();
+ let string_bytes = "Hello!".as_bytes();
+ buffer.extend_from_slice(string_bytes);
+
+ // Write a new buffer into the desc_chain
+ desc_chain.memory().write_slice(&buffer, desc_addr).unwrap();
+
+ // Verify that it is written
+ let mut read_buffer: Vec<u8> = vec![0; 0x200];
+ desc_chain
+ .memory()
+ .read_slice(&mut read_buffer, desc_addr)
+ .expect("Failed to read");
+ let read_buffer: Vec<u8> = read_buffer.iter().take(buffer.len()).copied().collect();
+
+ assert_eq!(
+ String::from_utf8(read_buffer).unwrap(),
+ String::from_utf8(buffer).unwrap()
+ );
+
+ assert!(vu_console_backend
+ .process_tx_requests(vec![desc_chain], &vring)
+ .is_ok());
+ }
+
+ #[test]
+ fn test_virtio_console_tx_request_network() {
+ let console_controller =
+ Arc::new(RwLock::new(ConsoleController::new(BackendType::Network)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+
+ // Test: Fill message to the buffer
+ vu_console_backend.update_memory(mem1).unwrap();
+ let desc_addr = desc_chain.clone().collect::<Vec<_>>()[0].addr();
+
+ // Build the Vec with the desired string
+ let mut buffer: Vec<u8> = Vec::new();
+ let string_bytes = "Hello!".as_bytes();
+ buffer.extend_from_slice(string_bytes);
+
+ // Write a new buffer into the desc_chain
+ desc_chain.memory().write_slice(&buffer, desc_addr).unwrap();
+
+ // Verify that it is written
+ let mut read_buffer: Vec<u8> = vec![0; 0x200];
+ desc_chain
+ .memory()
+ .read_slice(&mut read_buffer, desc_addr)
+ .expect("Failed to read");
+ let read_buffer: Vec<u8> = read_buffer.iter().take(buffer.len()).copied().collect();
+
+ assert_eq!(
+ String::from_utf8(read_buffer).unwrap(),
+ String::from_utf8(buffer).unwrap()
+ );
+
+ assert!(vu_console_backend
+ .process_tx_requests(vec![desc_chain], &vring)
+ .is_ok());
+ }
+
+ #[test]
+ fn test_virtio_console_rx_request() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend");
+
+ // Artificial memory
+ let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
+
+ // Test 1: Empty queue
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1, 0x1000).unwrap();
+ assert!(vu_console_backend
+ .process_rx_requests(vec![], &vring)
+ .is_ok());
+
+ // Test 2: Empty buffer
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+ assert!(!vu_console_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap());
+
+ // Test 3: Fill message to the buffer. The descriptor should be write-only
+ let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+
+ let input_buffer = "Hello!".to_string();
+ let _ = vu_console_backend.rx_data_fifo.add(input_buffer.clone());
+ assert_eq!(
+ vu_console_backend
+ .process_rx_requests(vec![desc_chain], &vring)
+ .unwrap_err(),
+ Error::DescriptorWriteFailed
+ );
+
+ // Test 4: Fill message to the buffer. Everything should work!
+ let desc_chain = build_desc_chain(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
+ let mem1 = GuestMemoryAtomic::new(mem.clone());
+ let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
+ vu_console_backend.update_memory(mem1).unwrap();
+
+ let input_buffer = "Hello!".to_string();
+ let _ = vu_console_backend.rx_data_fifo.add(input_buffer.clone());
+ assert!(vu_console_backend
+ .process_rx_requests(vec![desc_chain.clone()], &vring)
+ .unwrap());
+
+ // Test 5: Verify written data
+ let desc_addr = GuestAddress(0x100);
+ let mut read_buffer: Vec<u8> = vec![0; 0x100];
+ desc_chain
+ .memory()
+ .read_slice(&mut read_buffer, desc_addr)
+ .expect("Failed to read");
+
+ let read_buffer: Vec<u8> = read_buffer
+ .iter()
+ .take(input_buffer.len())
+ .copied()
+ .collect();
+
+ assert_eq!(String::from_utf8(read_buffer).unwrap(), input_buffer);
+ }
+
+ #[test]
+ fn test_virtio_console_start_tcp_console_thread() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let vu_console_backend = Arc::new(RwLock::new(
+ VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend"),
+ ));
+ let tcp_addr = "127.0.0.1:12345".to_string();
+
+ let read_handle = VhostUserConsoleBackend::start_tcp_console_thread(
+ &vu_console_backend,
+ tcp_addr.clone(),
+ );
+ vu_console_backend.read().unwrap().kill_console_thread();
+ assert!(read_handle.join().is_ok());
+ }
+
+ #[test]
+ fn test_virtio_console_start_nested_console_thread() {
+ let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
+ let vu_console_backend = Arc::new(RwLock::new(
+ VhostUserConsoleBackend::new(console_controller)
+ .expect("Failed create vhuconsole backend"),
+ ));
+
+ let read_handle = VhostUserConsoleBackend::start_console_thread(&vu_console_backend);
+
+ vu_console_backend.read().unwrap().kill_console_thread();
+ assert!(read_handle.join().is_ok());
}
}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/virtio_console.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/virtio_console.rs
new file mode 100755
index 00000000..323473f6
--- /dev/null
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/virtio_console.rs
@@ -0,0 +1,60 @@
+// Console virtio bindings
+//
+// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use vm_memory::{ByteValued, Le16, Le32};
+
+/// Feature bit numbers
+#[allow(dead_code)]
+pub const VIRTIO_CONSOLE_F_SIZE: u16 = 0;
+pub const VIRTIO_CONSOLE_F_MULTIPORT: u16 = 1;
+#[allow(dead_code)]
+pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u16 = 2;
+
+/// Console virtio control messages
+pub const VIRTIO_CONSOLE_DEVICE_READY: u16 = 0;
+pub const VIRTIO_CONSOLE_PORT_ADD: u16 = 1;
+#[allow(dead_code)]
+pub const VIRTIO_CONSOLE_PORT_REMOVE: u16 = 2;
+pub const VIRTIO_CONSOLE_PORT_READY: u16 = 3;
+pub const VIRTIO_CONSOLE_CONSOLE_PORT: u16 = 4;
+#[allow(dead_code)]
+pub const VIRTIO_CONSOLE_RESIZE: u16 = 5;
+pub const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6;
+pub const VIRTIO_CONSOLE_PORT_NAME: u16 = 7;
+
+/// Virtio Console Config
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub(crate) struct VirtioConsoleConfig {
+ pub cols: Le16,
+ pub rows: Le16,
+ pub max_nr_ports: Le32,
+ pub emerg_wr: Le32,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioConsoleConfig {}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub(crate) struct VirtioConsoleControl {
+ pub id: Le32,
+ pub event: Le16,
+ pub value: Le16,
+}
+
+impl VirtioConsoleControl {
+ pub fn to_le_bytes(self) -> Vec<u8> {
+ let mut buffer = Vec::new();
+
+ buffer.extend_from_slice(&self.id.to_native().to_le_bytes());
+ buffer.extend_from_slice(&self.event.to_native().to_le_bytes());
+ buffer.extend_from_slice(&self.value.to_native().to_le_bytes());
+ buffer
+ }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc
index 6026714e..df6d2ae1 100644
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc
@@ -1,87 +1,292 @@
+# Autogenerated with 'bitbake -c update_crates vhost-device-console'
+
+# from Cargo.lock
SRC_URI += " \
- crate://crates.io/encode_unicode/0.3.6 \
- crate://crates.io/unicode-width/0.1.11 \
- crate://crates.io/console/0.14.1 \
- crate://crates.io/lazy_static/1.4.0 \
- 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/aho-corasick/1.1.3 \
+ crate://crates.io/anstream/0.6.15 \
+ crate://crates.io/anstyle/1.0.8 \
+ crate://crates.io/anstyle-parse/0.2.5 \
+ crate://crates.io/anstyle-query/1.1.1 \
+ crate://crates.io/anstyle-wincon/3.0.4 \
+ crate://crates.io/anyhow/1.0.87 \
+ crate://crates.io/arc-swap/1.7.1 \
crate://crates.io/assert_matches/1.5.0 \
+ crate://crates.io/autocfg/1.3.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/bitflags/2.6.0 \
+ crate://crates.io/byte_conv/0.1.1 \
+ crate://crates.io/byteorder/1.5.0 \
+ crate://crates.io/cfg-if/1.0.0 \
+ crate://crates.io/clap/4.5.17 \
+ crate://crates.io/clap_builder/4.5.17 \
+ crate://crates.io/clap_derive/4.5.13 \
+ crate://crates.io/clap_lex/0.7.2 \
+ crate://crates.io/colorchoice/1.0.2 \
+ crate://crates.io/console/0.15.8 \
+ crate://crates.io/crossterm/0.27.0 \
+ crate://crates.io/crossterm_winapi/0.9.1 \
+ crate://crates.io/either/1.13.0 \
+ crate://crates.io/embedded-can/0.4.1 \
+ crate://crates.io/encode_unicode/0.3.6 \
+ crate://crates.io/enumn/0.1.14 \
+ crate://crates.io/env_filter/0.1.2 \
+ crate://crates.io/env_logger/0.10.2 \
+ crate://crates.io/env_logger/0.11.5 \
+ crate://crates.io/epoll/4.3.3 \
+ crate://crates.io/equivalent/1.0.1 \
+ crate://crates.io/errno/0.3.9 \
+ crate://crates.io/fastrand/2.1.1 \
+ crate://crates.io/futures/0.3.30 \
+ crate://crates.io/futures-channel/0.3.30 \
+ crate://crates.io/futures-core/0.3.30 \
+ crate://crates.io/futures-executor/0.3.30 \
+ crate://crates.io/futures-io/0.3.30 \
+ crate://crates.io/futures-macro/0.3.30 \
+ crate://crates.io/futures-sink/0.3.30 \
+ crate://crates.io/futures-task/0.3.30 \
+ crate://crates.io/futures-timer/3.0.3 \
+ crate://crates.io/futures-util/0.3.30 \
+ crate://crates.io/glob/0.3.1 \
+ crate://crates.io/hashbrown/0.14.5 \
+ crate://crates.io/heck/0.5.0 \
+ crate://crates.io/hermit-abi/0.3.9 \
+ crate://crates.io/hermit-abi/0.4.0 \
+ crate://crates.io/hex/0.4.3 \
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/indexmap/2.5.0 \
+ crate://crates.io/is-terminal/0.4.13 \
+ crate://crates.io/is_terminal_polyfill/1.70.1 \
+ crate://crates.io/itertools/0.10.5 \
+ crate://crates.io/lazy_static/1.5.0 \
+ crate://crates.io/libc/0.2.158 \
+ crate://crates.io/linux-raw-sys/0.4.14 \
+ crate://crates.io/lock_api/0.4.12 \
+ crate://crates.io/log/0.4.22 \
+ crate://crates.io/memchr/2.7.4 \
+ crate://crates.io/memoffset/0.7.1 \
+ crate://crates.io/mio/0.8.11 \
+ crate://crates.io/nb/1.1.0 \
+ crate://crates.io/neli/0.6.4 \
+ crate://crates.io/neli-proc-macros/0.1.3 \
+ crate://crates.io/nix/0.26.4 \
+ crate://crates.io/nix/0.27.1 \
+ crate://crates.io/num_cpus/1.16.0 \
+ crate://crates.io/num_enum/0.7.3 \
+ crate://crates.io/num_enum_derive/0.7.3 \
+ crate://crates.io/once_cell/1.19.0 \
+ crate://crates.io/parking_lot/0.12.3 \
+ crate://crates.io/parking_lot_core/0.9.10 \
+ crate://crates.io/pin-project-lite/0.2.14 \
+ crate://crates.io/pin-utils/0.1.0 \
+ crate://crates.io/proc-macro-crate/3.2.0 \
+ crate://crates.io/proc-macro2/1.0.86 \
+ crate://crates.io/queues/1.1.0 \
+ crate://crates.io/quote/1.0.37 \
+ crate://crates.io/redox_syscall/0.5.3 \
+ crate://crates.io/regex/1.10.6 \
+ crate://crates.io/regex-automata/0.4.7 \
+ crate://crates.io/regex-syntax/0.8.4 \
+ crate://crates.io/relative-path/1.9.3 \
+ crate://crates.io/rstest/0.22.0 \
+ crate://crates.io/rstest_macros/0.22.0 \
+ crate://crates.io/rustc_version/0.4.1 \
+ crate://crates.io/rustix/0.38.36 \
+ crate://crates.io/scopeguard/1.2.0 \
+ crate://crates.io/semver/1.0.23 \
+ crate://crates.io/serde/1.0.210 \
+ crate://crates.io/serde_derive/1.0.210 \
+ crate://crates.io/signal-hook/0.3.17 \
+ crate://crates.io/signal-hook-mio/0.2.4 \
+ crate://crates.io/signal-hook-registry/1.4.2 \
+ crate://crates.io/slab/0.4.9 \
+ crate://crates.io/smallvec/1.13.2 \
+ crate://crates.io/socket2/0.5.7 \
+ crate://crates.io/strsim/0.11.1 \
+ crate://crates.io/syn/1.0.109 \
+ crate://crates.io/syn/2.0.77 \
+ crate://crates.io/tempfile/3.12.0 \
+ crate://crates.io/termcolor/1.4.1 \
+ crate://crates.io/thiserror/1.0.63 \
+ crate://crates.io/thiserror-impl/1.0.63 \
+ crate://crates.io/toml_datetime/0.6.8 \
+ crate://crates.io/toml_edit/0.22.20 \
+ crate://crates.io/unicode-ident/1.0.13 \
+ crate://crates.io/unicode-width/0.1.13 \
+ crate://crates.io/utf8parse/0.2.2 \
+ crate://crates.io/vhost/0.11.0 \
+ crate://crates.io/vhost-user-backend/0.15.0 \
+ crate://crates.io/virtio-bindings/0.2.3 \
+ crate://crates.io/virtio-queue/0.12.0 \
+ crate://crates.io/vm-memory/0.14.1 \
+ crate://crates.io/vmm-sys-util/0.12.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-util/0.1.9 \
crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \
- crate://crates.io/windows-sys/0.45.0 \
- crate://crates.io/windows-targets/0.42.1 \
- crate://crates.io/windows_aarch64_gnullvm/0.42.1 \
- crate://crates.io/windows_aarch64_msvc/0.42.1 \
- crate://crates.io/windows_i686_gnu/0.42.1 \
- crate://crates.io/windows_i686_msvc/0.42.1 \
- crate://crates.io/windows_x86_64_gnu/0.42.1 \
- crate://crates.io/windows_x86_64_gnullvm/0.42.1 \
- crate://crates.io/windows_x86_64_msvc/0.42.1 \
+ crate://crates.io/windows-sys/0.48.0 \
+ crate://crates.io/windows-sys/0.52.0 \
+ crate://crates.io/windows-sys/0.59.0 \
+ crate://crates.io/windows-targets/0.48.5 \
+ crate://crates.io/windows-targets/0.52.6 \
+ crate://crates.io/windows_aarch64_gnullvm/0.48.5 \
+ crate://crates.io/windows_aarch64_gnullvm/0.52.6 \
+ crate://crates.io/windows_aarch64_msvc/0.48.5 \
+ crate://crates.io/windows_aarch64_msvc/0.52.6 \
+ crate://crates.io/windows_i686_gnu/0.48.5 \
+ crate://crates.io/windows_i686_gnu/0.52.6 \
+ crate://crates.io/windows_i686_gnullvm/0.52.6 \
+ crate://crates.io/windows_i686_msvc/0.48.5 \
+ crate://crates.io/windows_i686_msvc/0.52.6 \
+ crate://crates.io/windows_x86_64_gnu/0.48.5 \
+ crate://crates.io/windows_x86_64_gnu/0.52.6 \
+ crate://crates.io/windows_x86_64_gnullvm/0.48.5 \
+ crate://crates.io/windows_x86_64_gnullvm/0.52.6 \
+ crate://crates.io/windows_x86_64_msvc/0.48.5 \
+ crate://crates.io/windows_x86_64_msvc/0.52.6 \
+ crate://crates.io/winnow/0.6.18 \
"
+SRC_URI[aho-corasick-1.1.3.sha256sum] = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+SRC_URI[anstream-0.6.15.sha256sum] = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+SRC_URI[anstyle-1.0.8.sha256sum] = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+SRC_URI[anstyle-parse-0.2.5.sha256sum] = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+SRC_URI[anstyle-query-1.1.1.sha256sum] = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+SRC_URI[anstyle-wincon-3.0.4.sha256sum] = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+SRC_URI[anyhow-1.0.87.sha256sum] = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8"
+SRC_URI[arc-swap-1.7.1.sha256sum] = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+SRC_URI[assert_matches-1.5.0.sha256sum] = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
+SRC_URI[autocfg-1.3.0.sha256sum] = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+SRC_URI[bitflags-1.3.2.sha256sum] = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+SRC_URI[bitflags-2.6.0.sha256sum] = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+SRC_URI[byte_conv-0.1.1.sha256sum] = "649972315d4931137a26fc2bf3ca95ee257ad796a5b57bdeb04205c91a4b5780"
+SRC_URI[byteorder-1.5.0.sha256sum] = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+SRC_URI[cfg-if-1.0.0.sha256sum] = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+SRC_URI[clap-4.5.17.sha256sum] = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
+SRC_URI[clap_builder-4.5.17.sha256sum] = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
+SRC_URI[clap_derive-4.5.13.sha256sum] = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
+SRC_URI[clap_lex-0.7.2.sha256sum] = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+SRC_URI[colorchoice-1.0.2.sha256sum] = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+SRC_URI[console-0.15.8.sha256sum] = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
+SRC_URI[crossterm-0.27.0.sha256sum] = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
+SRC_URI[crossterm_winapi-0.9.1.sha256sum] = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+SRC_URI[either-1.13.0.sha256sum] = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+SRC_URI[embedded-can-0.4.1.sha256sum] = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438"
+SRC_URI[encode_unicode-0.3.6.sha256sum] = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
+SRC_URI[enumn-0.1.14.sha256sum] = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+SRC_URI[env_filter-0.1.2.sha256sum] = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
+SRC_URI[env_logger-0.10.2.sha256sum] = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+SRC_URI[env_logger-0.11.5.sha256sum] = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
+SRC_URI[epoll-4.3.3.sha256sum] = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79"
+SRC_URI[equivalent-1.0.1.sha256sum] = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+SRC_URI[errno-0.3.9.sha256sum] = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+SRC_URI[fastrand-2.1.1.sha256sum] = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+SRC_URI[futures-0.3.30.sha256sum] = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+SRC_URI[futures-channel-0.3.30.sha256sum] = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+SRC_URI[futures-core-0.3.30.sha256sum] = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+SRC_URI[futures-executor-0.3.30.sha256sum] = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+SRC_URI[futures-io-0.3.30.sha256sum] = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+SRC_URI[futures-macro-0.3.30.sha256sum] = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+SRC_URI[futures-sink-0.3.30.sha256sum] = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+SRC_URI[futures-task-0.3.30.sha256sum] = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+SRC_URI[futures-timer-3.0.3.sha256sum] = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+SRC_URI[futures-util-0.3.30.sha256sum] = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+SRC_URI[glob-0.3.1.sha256sum] = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+SRC_URI[hashbrown-0.14.5.sha256sum] = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+SRC_URI[heck-0.5.0.sha256sum] = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+SRC_URI[hermit-abi-0.3.9.sha256sum] = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+SRC_URI[hermit-abi-0.4.0.sha256sum] = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+SRC_URI[hex-0.4.3.sha256sum] = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+SRC_URI[humantime-2.1.0.sha256sum] = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+SRC_URI[indexmap-2.5.0.sha256sum] = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
+SRC_URI[is-terminal-0.4.13.sha256sum] = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+SRC_URI[is_terminal_polyfill-1.70.1.sha256sum] = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+SRC_URI[itertools-0.10.5.sha256sum] = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+SRC_URI[lazy_static-1.5.0.sha256sum] = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+SRC_URI[libc-0.2.158.sha256sum] = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+SRC_URI[linux-raw-sys-0.4.14.sha256sum] = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+SRC_URI[lock_api-0.4.12.sha256sum] = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+SRC_URI[log-0.4.22.sha256sum] = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+SRC_URI[memchr-2.7.4.sha256sum] = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+SRC_URI[memoffset-0.7.1.sha256sum] = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
+SRC_URI[mio-0.8.11.sha256sum] = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+SRC_URI[nb-1.1.0.sha256sum] = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+SRC_URI[neli-0.6.4.sha256sum] = "1100229e06604150b3becd61a4965d5c70f3be1759544ea7274166f4be41ef43"
+SRC_URI[neli-proc-macros-0.1.3.sha256sum] = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4"
+SRC_URI[nix-0.26.4.sha256sum] = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+SRC_URI[nix-0.27.1.sha256sum] = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
+SRC_URI[num_cpus-1.16.0.sha256sum] = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+SRC_URI[num_enum-0.7.3.sha256sum] = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+SRC_URI[num_enum_derive-0.7.3.sha256sum] = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+SRC_URI[once_cell-1.19.0.sha256sum] = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+SRC_URI[parking_lot-0.12.3.sha256sum] = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+SRC_URI[parking_lot_core-0.9.10.sha256sum] = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+SRC_URI[pin-project-lite-0.2.14.sha256sum] = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+SRC_URI[pin-utils-0.1.0.sha256sum] = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+SRC_URI[proc-macro-crate-3.2.0.sha256sum] = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+SRC_URI[proc-macro2-1.0.86.sha256sum] = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+SRC_URI[queues-1.1.0.sha256sum] = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0"
+SRC_URI[quote-1.0.37.sha256sum] = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+SRC_URI[redox_syscall-0.5.3.sha256sum] = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+SRC_URI[regex-1.10.6.sha256sum] = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+SRC_URI[regex-automata-0.4.7.sha256sum] = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+SRC_URI[regex-syntax-0.8.4.sha256sum] = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+SRC_URI[relative-path-1.9.3.sha256sum] = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
+SRC_URI[rstest-0.22.0.sha256sum] = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936"
+SRC_URI[rstest_macros-0.22.0.sha256sum] = "c5e1711e7d14f74b12a58411c542185ef7fb7f2e7f8ee6e2940a883628522b42"
+SRC_URI[rustc_version-0.4.1.sha256sum] = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+SRC_URI[rustix-0.38.36.sha256sum] = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36"
+SRC_URI[scopeguard-1.2.0.sha256sum] = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+SRC_URI[semver-1.0.23.sha256sum] = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+SRC_URI[serde-1.0.210.sha256sum] = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+SRC_URI[serde_derive-1.0.210.sha256sum] = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+SRC_URI[signal-hook-0.3.17.sha256sum] = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+SRC_URI[signal-hook-mio-0.2.4.sha256sum] = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
+SRC_URI[signal-hook-registry-1.4.2.sha256sum] = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+SRC_URI[slab-0.4.9.sha256sum] = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+SRC_URI[smallvec-1.13.2.sha256sum] = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+SRC_URI[socket2-0.5.7.sha256sum] = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+SRC_URI[strsim-0.11.1.sha256sum] = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+SRC_URI[syn-1.0.109.sha256sum] = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+SRC_URI[syn-2.0.77.sha256sum] = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+SRC_URI[tempfile-3.12.0.sha256sum] = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+SRC_URI[termcolor-1.4.1.sha256sum] = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+SRC_URI[thiserror-1.0.63.sha256sum] = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+SRC_URI[thiserror-impl-1.0.63.sha256sum] = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+SRC_URI[toml_datetime-0.6.8.sha256sum] = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+SRC_URI[toml_edit-0.22.20.sha256sum] = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
+SRC_URI[unicode-ident-1.0.13.sha256sum] = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+SRC_URI[unicode-width-0.1.13.sha256sum] = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+SRC_URI[utf8parse-0.2.2.sha256sum] = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+SRC_URI[vhost-0.11.0.sha256sum] = "6be08d1166d41a78861ad50212ab3f9eca0729c349ac3a7a8f557c62406b87cc"
+SRC_URI[vhost-user-backend-0.15.0.sha256sum] = "1f0ffb1dd8e00a708a0e2c32d5efec5812953819888591fff9ff68236b8a5096"
+SRC_URI[virtio-bindings-0.2.3.sha256sum] = "68d0df4f5ad79b1dc81b5913ac737e24a84dcd5100f36ed953a1faec18aba241"
+SRC_URI[virtio-queue-0.12.0.sha256sum] = "07d8406e7250c934462de585d8f2d2781c31819bca1fbb7c5e964ca6bbaabfe8"
+SRC_URI[vm-memory-0.14.1.sha256sum] = "3c3aba5064cc5f6f7740cddc8dae34d2d9a311cac69b60d942af7f3ab8fc49f4"
+SRC_URI[vmm-sys-util-0.12.1.sha256sum] = "1d1435039746e20da4f8d507a72ee1b916f7b4b05af7a91c093d2c6561934ede"
+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.9.sha256sum] = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+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-sys-0.52.0.sha256sum] = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+SRC_URI[windows-sys-0.59.0.sha256sum] = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+SRC_URI[windows-targets-0.48.5.sha256sum] = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+SRC_URI[windows-targets-0.52.6.sha256sum] = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+SRC_URI[windows_aarch64_gnullvm-0.48.5.sha256sum] = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+SRC_URI[windows_aarch64_gnullvm-0.52.6.sha256sum] = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+SRC_URI[windows_aarch64_msvc-0.48.5.sha256sum] = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+SRC_URI[windows_aarch64_msvc-0.52.6.sha256sum] = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+SRC_URI[windows_i686_gnu-0.48.5.sha256sum] = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+SRC_URI[windows_i686_gnu-0.52.6.sha256sum] = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+SRC_URI[windows_i686_gnullvm-0.52.6.sha256sum] = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+SRC_URI[windows_i686_msvc-0.48.5.sha256sum] = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+SRC_URI[windows_i686_msvc-0.52.6.sha256sum] = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+SRC_URI[windows_x86_64_gnu-0.48.5.sha256sum] = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+SRC_URI[windows_x86_64_gnu-0.52.6.sha256sum] = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+SRC_URI[windows_x86_64_gnullvm-0.48.5.sha256sum] = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+SRC_URI[windows_x86_64_gnullvm-0.52.6.sha256sum] = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+SRC_URI[windows_x86_64_msvc-0.48.5.sha256sum] = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+SRC_URI[windows_x86_64_msvc-0.52.6.sha256sum] = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+SRC_URI[winnow-0.6.18.sha256sum] = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb
index e02133bd..5fa2801d 100644
--- a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb
+++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb
@@ -7,8 +7,7 @@ EXTRAPATHS:prepend := "${THISDIR}:"
SRC_URI = " file://. "
-LICENSE = "Apache-2.0"
-LICENSE = "BSD-3-Clause"
+LICENSE = "Apache-2.0 | BSD-3-Clause"
LIC_FILES_CHKSUM = "\
file://LICENSE-APACHE;md5=3b83ef96387f14655fc854ddc3c6bd57 \
@@ -17,5 +16,8 @@ LIC_FILES_CHKSUM = "\
inherit cargo
inherit pkgconfig
+inherit cargo-update-recipe-crates
include vhost-device-console-crates.inc
+
+CARGO_BUILD_FLAGS = "-v --offline --target ${RUST_HOST_SYS} ${BUILD_MODE} --manifest-path=${CARGO_MANIFEST_PATH}"