Update vhost-device-console 67/30367/2
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Thu, 3 Oct 2024 08:09:37 +0000 (11:09 +0300)
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>
Tue, 22 Oct 2024 12:21:45 +0000 (12:21 +0000)
Bug-AGL: SPEC-4966
Change-Id: I41cbe30549ebe923f8e3000ece072aa66c5e90a9
Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
13 files changed:
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.lock [new file with mode: 0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs [changed mode: 0644->0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/virtio_console.rs [new file with mode: 0755]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb

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 (executable)
index 0000000..1486b38
--- /dev/null
@@ -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",
+]
old mode 100644 (file)
new mode 100755 (executable)
index c2f9f71..aa52036
@@ -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"] }
old mode 100644 (file)
new mode 100755 (executable)
index b2c9244..c9eba8a
@@ -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
old mode 100644 (file)
new mode 100755 (executable)
index 15b50a3..62d6745
@@ -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);
     }
 }
old mode 100644 (file)
new mode 100755 (executable)
index 2e2972b..079fe0c
@@ -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);
-    }
-}
-
old mode 100644 (file)
new mode 100755 (executable)
index cceafb4..216a01e
@@ -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);
+    }
 }
old mode 100644 (file)
new mode 100755 (executable)
index ebddb00..d5cca74
@@ -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 (executable)
index 0000000..323473f
--- /dev/null
@@ -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
+    }
+}
index 6026714..df6d2ae 100644 (file)
+# 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"
index e02133b..5fa2801 100644 (file)
@@ -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}"