diff options
Diffstat (limited to 'meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0')
11 files changed, 4526 insertions, 0 deletions
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md new file mode 100755 index 00000000..6c84888c --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## Unreleased + +### Added + +### Changed + +### Fixed + +### Deprecated + +## v0.1.0 + +First release + diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock new file mode 100755 index 00000000..50985ab2 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.lock @@ -0,0 +1,1049 @@ +# 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.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +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.72", +] + +[[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 = "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 = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[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.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.72", +] + +[[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 = "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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[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 = "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 = "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 = "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", + "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.72", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +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 = "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.72", + "unicode-ident", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[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.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[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.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +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 = "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.72", +] + +[[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.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[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", + "log", + "queues", + "socketcan", + "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", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878bcb1b2812a10c30d53b0ed054999de3d98f25ece91fc173973f9c57aaae86" + +[[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 = "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-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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[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.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml new file mode 100755 index 00000000..5890f7e1 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "vhost-device-can" +version = "0.1.0" +authors = ["Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>"] +description = "vhost can backend device" +repository = "https://github.com/rust-vmm/vhost-device" +readme = "README.md" +keywords = ["can", "vhost", "virt", "backend"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + +[dependencies] +clap = { version = "4.5", features = ["derive"] } +env_logger = "0.11" +log = "0.4" +thiserror = "1.0" +queues = "1.0.2" +# TODO: Update socketcan to version "v3.4.0" when this is released. +# Socketcan "v3.3.0" includes the following issue: https://github.com/socketcan-rs/socketcan-rs/pull/61. +# The version was set to the commit "f004ee91e142a" where that issue has been resolved. As soon as, a +# newer version is released we need to point socketcan dependency to it. +# NOTE: If you are using rust version "1.80", the compiler might complain about "std::mem::size_of". +# The solution to that problem is described in the following link: +# - https://github.com/socketcan-rs/socketcan-rs/pull/72 +socketcan = { path = "../git/" } +vhost = { version = "0.11", features = ["vhost-user-backend"] } +vhost-user-backend = { version = "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.12", features = ["test-utils"] } +vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic"] } diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE new file mode 100755 index 00000000..d6456956 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause new file mode 100755 index 00000000..dd975d98 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause @@ -0,0 +1,26 @@ +Copyright 2022 The rust-vmm authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md new file mode 100755 index 00000000..ade79128 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/README.md @@ -0,0 +1,147 @@ +# vhost-device-can - CAN emulation backend daemon + +## Description +This program is a vhost-user backend that emulates a VirtIO CAN device. +The device's binary takes two (2) parameters: a socket, a 'can-devices' list. +The socket is commonly used across all vhost-devices to communicate with +the vhost-user frontend device. The 'can-devices' represents a list of +CAN/FD devices appears in the host system which vhost-device-can will +forward messages to and from the frontend side. + +This program is tested with QEMU's `vhost-user-device-pci` device. +Examples' section below. + +## Synopsis +``` +**vhost-device-can** [*OPTIONS*] +```` + +## Options + +.. program:: vhost-device-can + +.. option:: -h, --help + + Print help. + +.. option:: -s, --socket-path=PATH + + Location of vhost-user Unix domain sockets, this path will be suffixed with + 0,1,2..socket_count-1. + +.. option:: -c, --socket-count=INT + + Number of guests (sockets) to attach to, default set to 1. + +.. option:: -d, --can-devices='CAN/FD interfaces' + + CAN/FD device list at the host OS in the format: + <can-_X_0> [<can_in_X_1>] ... [<can_in_X_N-1>] + + Note 1: Where N (the number of CAN/FD interfaces) is equal with the number + provided via *socket_count* parameter. + + Example: --can-devices "can0 can1 can2" + +## Features +This device is still work-in-progress (WIP) and on [virtio-spec v1.4](https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/) is based +on virtio-can Linux's driver and QEMU's device presented in the following RFC: +- https://lwn.net/Articles/934187/ + +Vhost-device-can have be been tested in scenarios with multiple QEMU's VMs using +host's *CAN/FD* devices. + +## Limitations + +1) The transmission of a CAN/FD frame to a host interface always is done + synchronously. This means that regardless the negotiation or not of the + feature *VIRTIO_CAN_F_LATE_TX_ACK*, the backend will always wait for the + transmission of the frame and after will mark the transmission request + as used. +2) Does not check for undefined flags in CAN/FD frame when send and receive + a CAN/FD frame from the frontend (QEMU device). +3) The host's CAN/FD devices should be already in *UP* state before staring + the vhost-device-can (by using `ip link set can0 [up,down]`). + - The control messages does not actually change host's device state +4) Current version of the device has been tested only with *vcan* device. + +## Examples + +### Dependencies +For testing the device the required dependencies are: +- Linux: + - Integrate *virtio-can* driver implemented by OpenSynergy: + - https://lwn.net/Articles/934187/ + - Set `CONFIG_VIRTIO_CAN=y` +- QEMU + - Integrate *virtio-can* device implemented by OpenSynergy: + - https://lwn.net/Articles/934187/ + - Clone vhost-user-can QEMU device (optional): + - A new vhost-user-can device has been implemented in the following repo: + - https://github.com/virtualopensystems/qemu/tree/vhu-can-rfc + +### Test the device + +The daemon should be started first: +```shell +host# vhost-device-can --socket-path=can.sock --can-devices="vcan0" +``` + +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-can: +1) Using `vhost-user-device-pci` available upstream since QEMU `v8.2.0`: +```text +host# qemu-system \ + -m 4096 \ + -numa node,memdev=mem \ + -object memory-backend-memfd,id=mem,size=4G,share=on \ + -chardev socket,id=can0,path=/tmp/can.sock \ + -device vhost-user-device-pci,chardev=can0,virtio-id=36,num_vqs=3,config_size=16 \ + ... +``` +2) Using `vhost-user-can-pci`: +```text +host# qemu-system \ + -m 4096 \ + -numa node,memdev=mem \ + -object memory-backend-memfd,id=mem,size=4G,share=on \ + -chardev socket,path=/tmp/can.sock,id=can0 \ + -device vhost-user-can-pci,chardev=can0,id=can \ + ... +``` + +> Note: For testing this scenario the reader needs to clone the QEMU version +> from the following repo which implements `vhost-user-can` device: +> - https://github.com/virtualopensystems/qemu/tree/vhu-can-rfc + +### Multi-Guest case + +Run vhost-device-can as: +```text +./vhost-device-can --socket-path /tmp/can.sock --socket-count 2 --can-devices "vcan0 vcan1" +``` +This command will start the device and create two new sockets: */tmp/can.sock0* and */tmp/can.sock1*. + +From the other side we run two QEMU instances (VMs) with vhost-user-can: +```text +host# qemu-system \ + -m 4096 \ + -numa node,memdev=mem \ + -object memory-backend-memfd,id=mem,size=4G,share=on \ + -chardev socket,path=<SOCKET_PATH>,id=can0 \ + -device vhost-user-can-pci,chardev=can0,id=can \ + ... +``` +In the first instance of QEMU *SOCKET_PATH* would be: */tmp/can.sock0*, +and will use *can0* (host interface) as sender and receiver. The second +QEMU VM would have: *SOCKET_PATH* = */tmp/can.sock1*, and will use *can1* +as receiver and *can2* as sender. + +## License + +This project is licensed under either of + +- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0 +- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause) diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs new file mode 100755 index 00000000..ce8d8511 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/backend.rs @@ -0,0 +1,268 @@ +// VIRTIO CAN Emulation via vhost-user +// +// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use log::{error, info, warn}; +use std::any::Any; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::{Arc, RwLock}; +use std::thread; + +use thiserror::Error as ThisError; +use vhost_user_backend::VhostUserDaemon; +use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; + +use crate::can::CanController; +use crate::vhu_can::VhostUserCanBackend; + +pub(crate) type Result<T> = std::result::Result<T, Error>; + +#[derive(Debug, ThisError)] +/// Errors related to low level CAN helpers +pub(crate) enum Error { + #[error("Invalid socket count: {0}")] + SocketCountInvalid(usize), + #[error("Could not find can devices")] + CouldNotFindCANDevs, + #[error("Could not create can controller: {0}")] + CouldNotCreateCanController(crate::can::Error), + #[error("Could not create can controller output socket: {0}")] + FailCreateCanControllerSocket(crate::can::Error), + #[error("Could not create can backend: {0}")] + CouldNotCreateBackend(crate::vhu_can::Error), + #[error("Could not create daemon: {0}")] + CouldNotCreateDaemon(vhost_user_backend::Error), + #[error("Fatal error: {0}")] + ServeFailed(vhost_user_backend::Error), + #[error("Thread `{0}` panicked")] + ThreadPanic(String, Box<dyn Any + Send>), +} + +#[derive(PartialEq, Debug)] +pub struct VuCanConfig { + pub socket_path: PathBuf, + pub socket_count: u32, + pub can_devices: Vec<String>, +} + +impl VuCanConfig { + 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) + }; + + (0..self.socket_count).map(make_socket_path).collect() + } +} + +/// This is the public API through which an external program starts the +/// vhost-device-can backend server. +pub(crate) fn start_backend_server(socket: PathBuf, can_devs: String) -> Result<()> { + loop { + let controller = + CanController::new(can_devs.clone()).map_err(Error::CouldNotCreateCanController)?; + let lockable_controller = Arc::new(RwLock::new(controller)); + let vu_can_backend = Arc::new(RwLock::new( + VhostUserCanBackend::new(lockable_controller.clone()) + .map_err(Error::CouldNotCreateBackend)?, + )); + lockable_controller + .write() + .unwrap() + .open_can_socket() + .map_err(Error::FailCreateCanControllerSocket)?; + + let read_handle = CanController::start_read_thread(lockable_controller.clone()); + + let mut daemon = VhostUserDaemon::new( + String::from("vhost-device-can-backend"), + vu_can_backend.clone(), + GuestMemoryAtomic::new(GuestMemoryMmap::new()), + ) + .map_err(Error::CouldNotCreateDaemon)?; + + // Start the read thread -- need to handle it after termination + let vring_workers = daemon.get_epoll_handlers(); + vu_can_backend + .read() + .unwrap() + .set_vring_worker(&vring_workers[0]); + + daemon.serve(&socket).map_err(Error::ServeFailed)?; + + // Terminate the thread which reads CAN messages from "can_devs" + lockable_controller.write().unwrap().exit_read_thread(); + + // Wait for read thread to exit + match read_handle.join() { + Ok(_) => info!("The read thread returned successfully"), + Err(e) => warn!("The read thread returned the error: {:?}", e), + } + } +} + +pub fn start_backend(config: VuCanConfig) -> Result<()> { + let mut handles = HashMap::new(); + let (senders, receiver) = std::sync::mpsc::channel(); + + for (thread_id, (socket, can_devs)) in config + .generate_socket_paths() + .into_iter() + .zip(config.can_devices.iter().cloned()) + .map(|(a, b)| (a, b.to_string())) + .enumerate() + { + println!( + "thread_id: {}, socket: {:?}, can_devs: {:?}", + thread_id, socket, can_devs, + ); + + let name = format!("vhu-can-{}", can_devs); + let sender = senders.clone(); + let handle = thread::Builder::new() + .name(name.clone()) + .spawn(move || { + let result = + std::panic::catch_unwind(move || start_backend_server(socket, can_devs)); + + // Notify the main thread that we are done. + sender.send(thread_id).unwrap(); + + result.map_err(|e| Error::ThreadPanic(name, e))? + }) + .unwrap(); + handles.insert(thread_id, handle); + } + + while !handles.is_empty() { + let thread_id = receiver.recv().unwrap(); + handles + .remove(&thread_id) + .unwrap() + .join() + .map_err(std::panic::resume_unwind) + .unwrap()?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::backend::Error::FailCreateCanControllerSocket; + use crate::can::Error::SocketOpen; + use crate::CanArgs; + use assert_matches::assert_matches; + + #[test] + fn test_can_valid_configuration() { + let valid_args = CanArgs { + socket_path: "/tmp/vhost.sock".to_string().into(), + can_devices: "can0".to_string(), + socket_count: 1, + }; + + assert_matches!( + VuCanConfig::try_from(valid_args), + Err(Error::CouldNotFindCANDevs) + ); + } + + #[test] + fn test_can_valid_mult_device_configuration() { + let valid_args = CanArgs { + socket_path: "/tmp/vhost.sock".to_string().into(), + can_devices: "can0 can1".to_string(), + socket_count: 2, + }; + + assert_matches!( + VuCanConfig::try_from(valid_args), + Err(Error::CouldNotFindCANDevs) + ); + } + + #[test] + fn test_can_invalid_socket_configuration() { + let invalid_args = CanArgs { + socket_path: "/tmp/vhost.sock".to_string().into(), + can_devices: "can0".to_string(), + socket_count: 0, + }; + + assert_matches!( + VuCanConfig::try_from(invalid_args), + Err(Error::SocketCountInvalid(0)) + ); + } + + #[test] + fn test_can_invalid_mult_socket_configuration_1() { + let invalid_args = CanArgs { + socket_path: "/tmp/vhost.sock".to_string().into(), + can_devices: "can0".to_string(), + socket_count: 2, + }; + + assert_matches!( + VuCanConfig::try_from(invalid_args), + Err(Error::SocketCountInvalid(2)) + ); + } + + #[test] + fn test_can_invalid_mult_socket_configuration_2() { + let invalid_args = CanArgs { + socket_path: "/tmp/vhost.sock".to_string().into(), + can_devices: "can0 can1".to_string(), + socket_count: 1, + }; + + assert_matches!( + VuCanConfig::try_from(invalid_args), + Err(Error::SocketCountInvalid(1)) + ); + } + + #[test] + fn test_can_valid_configuration_start_backend_fail() { + // Instantiate the struct with the provided values + let config = VuCanConfig { + socket_path: PathBuf::from("/tmp/vhost.sock"), + socket_count: 1, + can_devices: vec!["can0".to_string()], + }; + + assert_matches!( + start_backend(config), + Err(FailCreateCanControllerSocket(SocketOpen)) + ); + } + + #[test] + fn test_can_valid_configuration_start_backend_server_fail() { + let socket_path = PathBuf::from("/tmp/vhost.sock"); + let can_devs = "can0".to_string(); + + assert_matches!( + start_backend_server(socket_path, can_devs), + Err(FailCreateCanControllerSocket(SocketOpen)) + ); + } +} diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs new file mode 100755 index 00000000..6fc03550 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/can.rs @@ -0,0 +1,393 @@ +// CAN backend device +// +// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use log::{error, info, trace, warn}; +use std::sync::{Arc, RwLock}; + +use std::thread::{spawn, JoinHandle}; +use thiserror::Error as ThisError; +use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; +extern crate queues; +use queues::*; +extern crate socketcan; +use crate::virtio_can::{ + VirtioCanConfig, VirtioCanFrame, CAN_CS_STARTED, CAN_CS_STOPPED, CAN_EFF_FLAG, + CAN_FRMF_TYPE_FD, VIRTIO_CAN_RX, +}; +use socketcan::{ + CanAnyFrame, CanDataFrame, CanFdFrame, CanFdSocket, EmbeddedFrame, ExtendedId, Frame, Id, + Socket, StandardId, +}; + +type Result<T> = std::result::Result<T, Error>; + +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +/// Errors related to low level can helpers +pub(crate) enum Error { + #[error("Can open socket operation failed")] + SocketOpen, + #[error("Can write socket operation failed")] + SocketWrite, + #[error("Can read socket operation failed")] + SocketRead, + #[error("Pop can element operation failed")] + PopFailed, + #[error("Queue is empty")] + QueueEmpty, + #[error("Creating Eventfd for CAN events failed")] + EventFdFailed, + #[error("Push can element operation failed")] + PushFailed, + #[error("No output interface available")] + NoOutputInterface, +} + +#[derive(Debug)] +pub(crate) struct CanController { + pub config: VirtioCanConfig, + pub can_name: String, + pub can_socket: Option<CanFdSocket>, + pub rx_event_fd: EventFd, + rx_fifo: Queue<VirtioCanFrame>, + pub status: bool, + pub ctrl_state: u8, +} + +impl CanController { + // Creates a new controller corresponding to `device`. + pub(crate) fn new(can_name: String) -> Result<CanController> { + let can_name = can_name.to_owned(); + info!("can_name: {:?}", can_name); + + let rx_fifo = Queue::new(); + let rx_efd = EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?; + + Ok(CanController { + config: VirtioCanConfig { status: 0x0.into() }, + can_name, + can_socket: None, + rx_event_fd: rx_efd, + rx_fifo, + status: true, + ctrl_state: CAN_CS_STOPPED, + }) + } + + pub fn print_can_frame(canframe: VirtioCanFrame) { + trace!("canframe.msg_type 0x{:x}", canframe.msg_type.to_native()); + trace!("canframe.can_id 0x{:x}", canframe.can_id.to_native()); + trace!("canframe.length {}", canframe.length.to_native()); + trace!("canframe.flags 0x{:x}", canframe.flags.to_native()); + if canframe.length.to_native() == 0 { + trace!("[]"); + return; + } + trace!("["); + let last_elem = canframe.length.to_native() as usize - 1; + for (index, sdu) in canframe.sdu.iter().enumerate() { + if index == last_elem { + trace!("0x{:x}", sdu); + break; + } + trace!("0x{:x}, ", sdu); + } + trace!("]"); + } + + pub fn start_read_thread(controller: Arc<RwLock<CanController>>) -> JoinHandle<Result<()>> { + spawn(move || CanController::read_can_socket(controller)) + } + + pub fn push(&mut self, rx_elem: VirtioCanFrame) -> Result<()> { + match self.rx_fifo.add(rx_elem) { + Ok(_) => Ok(()), + _ => Err(Error::PushFailed), + } + } + + pub fn rx_is_empty(&mut self) -> bool { + self.rx_fifo.size() == 0 + } + + pub fn pop(&mut self) -> Result<VirtioCanFrame> { + if self.rx_fifo.size() < 1 { + return Err(Error::QueueEmpty); + } + + match self.rx_fifo.remove() { + Ok(item) => Ok(item), + _ => Err(Error::PopFailed), + } + } + + pub fn open_can_socket(&mut self) -> Result<()> { + self.can_socket = match CanFdSocket::open(&self.can_name) { + Ok(socket) => Some(socket), + Err(_) => { + warn!("Error opening CAN socket"); + return Err(Error::SocketOpen); + } + }; + Ok(()) + } + + // Helper function to process frame + fn process_frame<F: Frame>(frame: F, is_fd: bool) -> VirtioCanFrame { + VirtioCanFrame { + msg_type: VIRTIO_CAN_RX.into(), + can_id: frame.id_word().into(), + length: (frame.data().len() as u16).into(), + reserved: 0.into(), + flags: if is_fd { + CAN_FRMF_TYPE_FD.into() + } else { + 0.into() + }, + sdu: { + let mut sdu_data: [u8; 64] = [0; 64]; + sdu_data[..frame.data().len()].copy_from_slice(frame.data()); + sdu_data + }, + } + } + + pub fn read_can_socket(controller: Arc<RwLock<CanController>>) -> Result<()> { + let can_name = &controller.read().unwrap().can_name.clone(); + dbg!("Start reading from {} socket!", &can_name); + let socket = match CanFdSocket::open(can_name) { + Ok(socket) => socket, + Err(_) => { + warn!("Error opening CAN socket"); + return Err(Error::SocketOpen); + } + }; + + // Set non-blocking otherwise the device will not restart immediatelly + // when the VM closes, and a new canfd message needs to be received for + // restart to happen. + // This caused by the fact that the thread is stacked in read function + // and does not go to the next loop to check the status condition. + socket + .set_nonblocking(true) + .expect("Cannot set nonblocking"); + + // Receive CAN messages + loop { + // If the status variable is false then break and exit. + if !controller.read().unwrap().status { + dbg!("exit read can thread"); + return Ok(()); + } + + if let Ok(frame) = socket.read_frame() { + // If ctrl_state is stopped, consume the received CAN/FD frame + // and loop till the ctrl_state changes to started or the thread + // to exit. + if controller.read().unwrap().ctrl_state != CAN_CS_STARTED { + trace!("CAN/FD frame is received but not saved!"); + continue; + } + + // Match and process frame variants + let read_can_frame = match frame { + CanAnyFrame::Normal(frame) => { + trace!("Received CAN frame: {:?}", frame); + Self::process_frame(frame, false) + } + CanAnyFrame::Fd(frame) => { + trace!("Received CAN FD frame: {:?}", frame); + Self::process_frame(frame, true) + } + CanAnyFrame::Remote(frame) => { + trace!("Received Remote CAN frame: {:?}", frame); + Self::process_frame(frame, false) + } + CanAnyFrame::Error(frame) => { + trace!("Received Error frame: {:?}", frame); + Self::process_frame(frame, false) + } + }; + + match controller.write().unwrap().push(read_can_frame) { + Ok(_) => warn!("New Can frame was received"), + Err(_) => { + warn!("Error read/push CAN frame"); + return Err(Error::SocketRead); + } + }; + + controller + .write() + .unwrap() + .rx_event_fd + .write(1) + .expect("Fail to write on rx_event_fd"); + } + } + } + + pub(crate) fn exit_read_thread(&mut self) { + trace!("Exit can read thread\n"); + self.status = false; + } + + pub(crate) fn config(&mut self) -> &VirtioCanConfig { + &self.config + } + + pub(crate) fn can_out(&self, tx_request: VirtioCanFrame) -> Result<()> { + // Create a CAN frame with a specific CAN-ID and the data buffer + let can_id: Id = if (tx_request.can_id.to_native() & CAN_EFF_FLAG) != 0 { + // SAFETY: Use new_unchecked cause checks have been taken place + // to prior stage. Also flags have beem already added on can_id + // so tnew will fail (can_id + can_flags) > 29 bits + unsafe { Id::Extended(ExtendedId::new_unchecked(tx_request.can_id.into())) } + } else { + // SAFETY: Use new_unchecked cause checks have been taken place + // to prior stage. Also flags have beem already added on can_id + // so tnew will fail (can_id + can_flags) > 11 bits + unsafe { + Id::Standard(StandardId::new_unchecked( + tx_request.can_id.to_native() as u16 + )) + } + }; + + // Grab the data to be tranfered + let data_len = tx_request.length.to_native() as usize; + let data: Vec<u8> = tx_request.sdu.iter().cloned().take(data_len).collect(); + + // Format CAN/FD frame + let frame: CanAnyFrame = if (tx_request.flags.to_native() & CAN_FRMF_TYPE_FD) != 0 { + CanAnyFrame::Fd(CanFdFrame::new(can_id, &data).expect("Fail to create CanFdFrame")) + } else { + CanAnyFrame::Normal(CanDataFrame::new(can_id, &data).expect("Fail to create CanFrame")) + }; + + // Send the CAN/FD frame + let socket = self.can_socket.as_ref().ok_or("No available device"); + + match socket { + Ok(socket) => match socket.write_frame(&frame) { + Ok(_) => Ok(()), + Err(_) => { + warn!("Error write CAN socket"); + Err(Error::SocketWrite) + } + }, + Err(_) => Err(Error::NoOutputInterface), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::vhu_can::VhostUserCanBackend; + use std::sync::{Arc, RwLock}; + + #[test] + fn test_can_controller_creation() { + let can_name = "can".to_string(); + + let controller = CanController::new(can_name.clone()).unwrap(); + assert_eq!(controller.can_name, can_name); + } + + #[test] + fn test_can_controller_push_and_pop() { + let can_name = "can".to_string(); + let mut controller = CanController::new(can_name.clone()).unwrap(); + + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_RX.into(), + can_id: 123.into(), + length: 64.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + // Test push + controller.push(frame).unwrap(); + + // Test pop + let pop_result = controller.pop().unwrap(); + assert_eq!(pop_result, frame); + } + + #[test] + fn test_can_controller_config() { + let can_name = "can".to_string(); + let mut controller = CanController::new(can_name.clone()).unwrap(); + + // Test config + let config = controller.config(); + assert_eq!(config.status.to_native(), 0); + } + + #[test] + fn test_can_controller_operation() { + let can_name = "can".to_string(); + let mut controller = CanController::new(can_name.clone()).unwrap(); + + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_RX.into(), + can_id: 123.into(), + length: 64.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + match controller.open_can_socket() { + Ok(_) => { + // Test operation + let operation_result = controller.can_out(frame); + assert!(operation_result.is_ok()); + } + Err(_) => warn!("There is no CAN interface with {} name", can_name), + } + } + + #[test] + fn test_can_controller_start_read_thread() { + let can_name = "can".to_string(); + let controller = CanController::new(can_name.clone()).unwrap(); + let arc_controller = Arc::new(RwLock::new(controller)); + + // Test start_read_thread + let thread_handle = CanController::start_read_thread(arc_controller.clone()); + assert!(thread_handle.join().is_ok()); + } + + #[test] + fn test_can_open_socket_fail() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + assert_eq!( + controller.write().unwrap().open_can_socket(), + Err(Error::SocketOpen) + ); + } + + #[test] + fn test_can_read_socket_fail() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + assert_eq!( + CanController::read_can_socket(controller), + Err(Error::SocketOpen) + ); + } +} diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs new file mode 100755 index 00000000..97386884 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs @@ -0,0 +1,96 @@ +// VIRTIO CAN Emulation via vhost-user +// +// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +mod backend; +mod can; +mod vhu_can; +mod virtio_can; + +use clap::Parser; +use log::{error, info}; +use socketcan::{CanSocket, Socket}; +use std::convert::TryFrom; +use std::path::PathBuf; +use std::process::exit; + +pub(crate) type Result<T> = std::result::Result<T, Error>; +use crate::backend::{start_backend, Error, VuCanConfig}; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct CanArgs { + /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1. + #[clap(short, long, value_name = "SOCKET")] + socket_path: PathBuf, + + /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.) + #[clap(short = 'd', long)] + can_devices: String, + + /// Number of guests (sockets) to connect to. + #[clap(short = 'c', long, default_value_t = 1)] + socket_count: u32, +} + +fn check_can_devices(can_devices: &[String]) -> Result<()> { + for can_dev in can_devices { + if CanSocket::open(can_dev).is_err() { + info!("There is no interface with the following name {}", can_dev); + return Err(Error::CouldNotFindCANDevs); + } + } + Ok(()) +} + +fn parse_can_devices(input: &CanArgs) -> Result<Vec<String>> { + let can_devices_vec: Vec<&str> = input.can_devices.split_whitespace().collect(); + let can_devices: Vec<_> = can_devices_vec.iter().map(|x| x.to_string()).collect(); + + if (can_devices.len() as u32) != input.socket_count { + info!( + "Number of CAN/FD devices ({}) not equal with socket count {}", + input.can_devices, input.socket_count + ); + return Err(Error::SocketCountInvalid( + input.socket_count.try_into().unwrap(), + )); + } + + match check_can_devices(&can_devices) { + Ok(_) => Ok(can_devices), + Err(_) => Err(Error::CouldNotFindCANDevs), + } +} + +impl TryFrom<CanArgs> for VuCanConfig { + type Error = Error; + + fn try_from(args: CanArgs) -> Result<Self> { + if args.socket_count == 0 { + return Err(Self::Error::SocketCountInvalid(0)); + } + + let can_devices = match parse_can_devices(&args) { + Ok(can_devs) => can_devs, + Err(e) => return Err(e), + }; + + Ok(VuCanConfig { + socket_path: args.socket_path, + socket_count: args.socket_count, + can_devices, + }) + } +} + +fn main() { + env_logger::init(); + if let Err(e) = VuCanConfig::try_from(CanArgs::parse()).and_then(start_backend) { + error!("{e}"); + exit(1); + } +} diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs new file mode 100755 index 00000000..b53f57d1 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/vhu_can.rs @@ -0,0 +1,2145 @@ +// vhost device can +// +// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use crate::can::CanController; +use crate::can::Error::QueueEmpty; +use crate::virtio_can::{ + VirtioCanCtrlRequest, VirtioCanFrame, VirtioCanHeader, CANFD_VALID_LENGTHS, CAN_CS_STARTED, + CAN_CS_STOPPED, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_BUSOFF, CAN_ERR_FLAG, CAN_FRMF_TYPE_FD, + CAN_RTR_FLAG, CAN_SFF_MASK, VIRTIO_CAN_FLAGS_EXTENDED, VIRTIO_CAN_FLAGS_FD, + VIRTIO_CAN_FLAGS_RTR, VIRTIO_CAN_FLAGS_VALID_MASK, VIRTIO_CAN_F_CAN_CLASSIC, + VIRTIO_CAN_F_CAN_FD, VIRTIO_CAN_F_RTR_FRAMES, VIRTIO_CAN_RESULT_NOT_OK, VIRTIO_CAN_RESULT_OK, + VIRTIO_CAN_RX, VIRTIO_CAN_SET_CTRL_MODE_START, VIRTIO_CAN_SET_CTRL_MODE_STOP, + VIRTIO_CAN_S_CTRL_BUSOFF, VIRTIO_CAN_TX, +}; +use log::{error, trace, warn}; +use std::os::fd::AsRawFd; +use std::slice::from_raw_parts; +use std::sync::{Arc, RwLock}; +use std::{ + convert, + io::{self, Result as IoResult}, +}; +use thiserror::Error as ThisError; +use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; +use vhost_user_backend::VringEpollHandler; +use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT}; +use virtio_bindings::bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}; +use virtio_bindings::bindings::virtio_ring::{ + VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, +}; +use virtio_queue::{DescriptorChain, QueueOwnedT}; +use vm_memory::{ + ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, +}; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; + +/// Virtio configuration +const QUEUE_SIZE: usize = 64; +const NUM_QUEUES: usize = 3; + +/// Queues +const TX_QUEUE: u16 = 0; +const RX_QUEUE: u16 = 1; +const CTRL_QUEUE: u16 = 2; +const BACKEND_EFD: u16 = (NUM_QUEUES + 1) as u16; + +type Result<T> = std::result::Result<T, Error>; + +#[derive(Copy, Clone, Debug, PartialEq, ThisError)] +/// Errors related to vhost-device-can-daemon. +pub(crate) enum Error { + #[error("Failed to handle event, didn't match EPOLLIN")] + HandleEventNotEpollIn, + #[error("Failed to handle unknown event")] + HandleEventUnknown, + #[error("Descriptor not found")] + DescriptorNotFound, + #[error("Descriptor read failed")] + DescriptorReadFailed, + #[error("Descriptor write failed")] + DescriptorWriteFailed, + #[error("Failed to send notification")] + NotificationFailed, + #[error("Failed to create new EventFd")] + EventFdFailed, + #[error("Unknown can message type: {0}")] + UnexpectedCanMsgType(u16), + #[error("RTR frames not negotiated")] + UnexpectedRtrFlag, + #[error("Can FD frames not negotiated")] + UnexpectedFdFlag, + #[error("Unexpected CAN ID bigger than: {0} bits")] + UnexpectedCanId(u16), + #[error("Invalid CAN flags: {0}")] + InvalidCanFlags(u32), + #[error("Invalid CAN[FD] length: {0}")] + InvalidCanLength(u32), + #[error("Classic CAN frames not negotiated")] + UnexpectedClassicFlag, + #[error("Bus off error received")] + BusoffRxFrame, + #[error("Rx CAN frame has unknown error")] + RxFrameUnknownFail, +} + +impl convert::From<Error> for io::Error { + fn from(e: Error) -> Self { + io::Error::new(io::ErrorKind::Other, e) + } +} + +pub(crate) struct VhostUserCanBackend { + controller: Arc<RwLock<CanController>>, + acked_features: u64, + event_idx: bool, + pub(crate) exit_event: EventFd, + mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>, +} + +type CanDescriptorChain = DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap<()>>>; + +impl VhostUserCanBackend { + pub(crate) fn new(controller: Arc<RwLock<CanController>>) -> Result<Self> { + Ok(VhostUserCanBackend { + controller, + event_idx: false, + acked_features: 0x0, + exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?, + mem: None, + }) + } + + fn check_features(&self, features: u16) -> bool { + (self.acked_features & (1 << features)) != 0 + } + + fn check_tx_frame(&self, request: VirtioCanFrame) -> Result<VirtioCanFrame> { + let msg_type = request.msg_type.to_native(); + let mut can_id = request.can_id.to_native(); + let length = request.length.to_native(); + let mut flags = 0; + + if msg_type != VIRTIO_CAN_TX { + warn!("TX: Message type 0x{:x} unknown\n", msg_type); + return Err(Error::UnexpectedCanMsgType(msg_type)); + } + + // Check for undefined bits in frame's flags + let invalid_can_flags: u32 = request.flags.to_native() & !VIRTIO_CAN_FLAGS_VALID_MASK; + if invalid_can_flags != 0 { + return Err(Error::InvalidCanFlags(invalid_can_flags)); + } + + // If VIRTIO_CAN_FLAGS_EXTENDED has negotiated then use extended CAN ID + if (request.flags.to_native() & VIRTIO_CAN_FLAGS_EXTENDED) != 0 { + // If we have a extended frame there is no way to check if + // can_id > 29 bits. The reason is that bit 29, 30, 31 are + // dedicated to CAN specific flags (EFF, RTR, ERR). + if can_id > CAN_EFF_MASK { + return Err(Error::UnexpectedCanId(29_u16)); + } + can_id |= CAN_EFF_FLAG; + } else { + // If we have a standard frame and can_id > 11 bits then fail + if can_id > CAN_SFF_MASK { + return Err(Error::UnexpectedCanId(11_u16)); + } + } + + // Remote transfer request is used only with classic CAN + if (request.flags.to_native() & VIRTIO_CAN_FLAGS_RTR) != 0 { + if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) + || !self.check_features(VIRTIO_CAN_F_RTR_FRAMES) + { + warn!("TX: RTR frames not negotiated"); + return Err(Error::UnexpectedRtrFlag); + } + can_id |= CAN_RTR_FLAG; + } + + // One of VIRTIO_CAN_F_CAN_CLASSIC and VIRTIO_CAN_F_CAN_FD must be negotiated + // Check if VIRTIO_CAN_F_CAN_FD is negotiated when the frame is CANFD + if (request.flags.to_native() & VIRTIO_CAN_FLAGS_FD) != 0 { + if !self.check_features(VIRTIO_CAN_F_CAN_FD) { + warn!("TX: FD frames not negotiated\n"); + return Err(Error::UnexpectedFdFlag); + } + flags = CAN_FRMF_TYPE_FD; + } else { + // Check if VIRTIO_CAN_F_CAN_CLASSIC is negotiated when the frame is CAN + if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) { + warn!("TX: Classic frames not negotiated\n"); + return Err(Error::UnexpectedClassicFlag); + } + } + + // Check if CAN/FD length is out-of-range (based on negotiated features) + if (request.flags.to_native() & VIRTIO_CAN_FLAGS_FD) != 0 { + if length > 8 && !CANFD_VALID_LENGTHS.contains(&length.into()) { + return Err(Error::InvalidCanLength(length.into())); + } + } else if length > 8 { + return Err(Error::InvalidCanLength(length.into())); + } + + Ok(VirtioCanFrame { + msg_type: msg_type.into(), + can_id: can_id.into(), + length: length.into(), + reserved: 0.into(), + flags: flags.into(), + sdu: request.sdu[0..64].try_into().unwrap(), + }) + } + + fn check_rx_frame(&self, response: VirtioCanFrame) -> Result<VirtioCanFrame> { + CanController::print_can_frame(response); + + let mut can_rx = VirtioCanFrame { + msg_type: VIRTIO_CAN_RX.into(), + can_id: response.can_id, + length: response.length, + reserved: 0.into(), + flags: response.flags, + sdu: [0; 64], + }; + let res_len = response.length.to_native(); + let res_can_id = response.can_id.to_native(); + let mut res_flags = response.flags.to_native(); + + // If we receive an error message check if that's a busoff. + // If no just drop the message, otherwise update config and return. + if (res_can_id & CAN_ERR_FLAG) != 0 { + if (res_can_id & CAN_ERR_BUSOFF) != 0 { + // TODO: Trigger a config_change notification + self.controller.write().unwrap().config.status = VIRTIO_CAN_S_CTRL_BUSOFF.into(); + self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED; + warn!("Got BusOff error frame, device does a local bus off\n"); + return Err(Error::BusoffRxFrame); + } else { + trace!("Dropping error frame 0x{:x}\n", response.can_id.to_native()); + return Err(Error::RxFrameUnknownFail); + } + } + + // One of VIRTIO_CAN_F_CAN_CLASSIC and VIRTIO_CAN_F_CAN_FD must be negotiated + if (res_flags & CAN_FRMF_TYPE_FD) != 0 { + if !self.check_features(VIRTIO_CAN_F_CAN_FD) { + warn!("Drop non-supported CAN FD frame"); + return Err(Error::UnexpectedFdFlag); + } + } else if !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) { + warn!("Drop non-supported CAN classic frame"); + return Err(Error::UnexpectedClassicFlag); + } + + // Add VIRTIO_CAN_FLAGS_EXTENDED in flag if the received frame + // had an extended CAN ID. + // Based on virtio-spec v1.4, the 3 MSB of can_id should be set to 0. + if (res_can_id & CAN_EFF_FLAG) != 0 { + can_rx.flags = VIRTIO_CAN_FLAGS_EXTENDED.into(); + can_rx.can_id = (res_can_id & CAN_EFF_MASK).into(); + } else { + can_rx.flags = 0.into(); + can_rx.can_id = (res_can_id & CAN_SFF_MASK).into(); + } + + // Remote transfer request is used only with classic CAN + if (res_can_id & CAN_RTR_FLAG) != 0 { + if !self.check_features(VIRTIO_CAN_F_RTR_FRAMES) + || !self.check_features(VIRTIO_CAN_F_CAN_CLASSIC) + { + warn!("Drop non-supported RTR frame"); + return Err(Error::UnexpectedRtrFlag); + } + // If remote transfer request is enabled add the according flag + can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_RTR).into(); + } + + // Treat Vcan interface as CANFD if MTU is set to 64 bytes. + // + // Vcan can not be configured as CANFD interface, but it is + // possible to configure its MTU to 64 bytes. So if a messages + // bigger than 8 bytes is being received we consider it as + // CANFD message. + let can_name = self.controller.read().unwrap().can_name.clone(); + if self.check_features(VIRTIO_CAN_F_CAN_FD) && res_len > 8 && can_name == "vcan0" { + res_flags |= CAN_FRMF_TYPE_FD; + warn!("\n\n\nCANFD VCAN0\n\n"); + } + + // Check if CAN/FD length is out-of-range (based on negotiated features) + if (res_flags & CAN_FRMF_TYPE_FD) != 0 { + if res_len > 8 && !CANFD_VALID_LENGTHS.contains(&res_len.into()) { + return Err(Error::InvalidCanLength(res_len.into())); + } + can_rx.flags = (can_rx.flags.to_native() | VIRTIO_CAN_FLAGS_FD).into(); + } else if res_len > 8 { + return Err(Error::InvalidCanLength(res_len.into())); + } + + can_rx.sdu.copy_from_slice(&response.sdu[0..64]); + CanController::print_can_frame(can_rx); + + Ok(can_rx) + } + + fn process_ctrl_requests( + &self, + requests: Vec<CanDescriptorChain>, + vring: &VringRwLock, + ) -> Result<bool> { + if requests.is_empty() { + return Ok(true); + } + + for desc_chain in requests { + let atomic_mem = self.mem.as_ref().unwrap().memory(); + + let mut reader = desc_chain + .clone() + .reader(&atomic_mem) + .map_err(|_| Error::DescriptorReadFailed)?; + + let mut writer = desc_chain + .clone() + .writer(&atomic_mem) + .map_err(|_| Error::DescriptorWriteFailed)?; + + let request = reader + .read_obj::<VirtioCanCtrlRequest>() + .map_err(|_| Error::DescriptorReadFailed)?; + + // This implementation requires the CAN devices to be already in UP state + // before starting. This code does not trigger state changes [UP/DOWN] of + // the host CAN devices. + let response = match request.msg_type.into() { + VIRTIO_CAN_SET_CTRL_MODE_START => { + if self.controller.write().unwrap().ctrl_state == CAN_CS_STARTED { + VIRTIO_CAN_RESULT_NOT_OK + } else { + // TODO: Trigger a config_change notification (optional) + self.controller.write().unwrap().config.status = 0.into(); + self.controller.write().unwrap().ctrl_state = CAN_CS_STARTED; + VIRTIO_CAN_RESULT_OK + } + } + VIRTIO_CAN_SET_CTRL_MODE_STOP => { + if self.controller.write().unwrap().ctrl_state == CAN_CS_STOPPED { + VIRTIO_CAN_RESULT_NOT_OK + } else { + // TODO: Trigger a config_change notification (optional) + self.controller.write().unwrap().config.status = 0.into(); + self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED; + VIRTIO_CAN_RESULT_OK + } + } + _ => { + trace!("Ctrl queue: msg type 0x{:?} unknown", request.msg_type); + VIRTIO_CAN_RESULT_NOT_OK + } + }; + + writer + .write_obj(response) + .map_err(|_| Error::DescriptorWriteFailed)?; + + if vring + .add_used(desc_chain.head_index(), writer.bytes_written() as u32) + .is_err() + { + warn!("Couldn't return used descriptors to the ring"); + } + } + + Ok(true) + } + + fn process_tx_requests( + &self, + requests: Vec<CanDescriptorChain>, + vring: &VringRwLock, + ) -> Result<bool> { + if requests.is_empty() { + return Ok(true); + } + + for desc_chain in requests { + let atomic_mem = self.mem.as_ref().unwrap().memory(); + + let mut reader = desc_chain + .clone() + .reader(&atomic_mem) + .map_err(|_| Error::DescriptorReadFailed)?; + + let mut writer = desc_chain + .clone() + .writer(&atomic_mem) + .map_err(|_| Error::DescriptorWriteFailed)?; + + let request_header = reader + .read_obj::<VirtioCanHeader>() + .map_err(|_| Error::DescriptorReadFailed)?; + + let data_len = request_header.length.to_native() as usize; + let mut request_data: Vec<u8> = Vec::new(); + for _i in 0..data_len { + let data_byte = reader + .read_obj::<u8>() + .map_err(|_| Error::DescriptorReadFailed)?; + request_data.push(data_byte); + } + + let request = VirtioCanFrame { + msg_type: request_header.msg_type, + can_id: request_header.can_id, + length: request_header.length, + reserved: request_header.reserved, + flags: request_header.flags, + sdu: { + let mut sdu_data: [u8; 64] = [0; 64]; + sdu_data[..request_header.length.to_native() as usize] + .copy_from_slice(request_data.as_slice()); + sdu_data + }, + }; + CanController::print_can_frame(request); + + let response = if self.controller.read().unwrap().ctrl_state == CAN_CS_STOPPED { + trace!("Device is stopped!"); + VIRTIO_CAN_RESULT_NOT_OK + } else { + let _response = match self.check_tx_frame(request) { + Ok(frame) => { + CanController::print_can_frame(frame); + + // If the VIRTIO_CAN_F_CAN_LATE_TX_ACK is negotiated sent the + // frame and wait for it to be sent. + // TODO: Otherwise send it asynchronously. + match self.controller.write().unwrap().can_out(frame) { + Ok(_) => VIRTIO_CAN_RESULT_OK, + Err(_) => { + warn!("we got an error from controller send func"); + VIRTIO_CAN_RESULT_NOT_OK + } + } + } + Err(e) => { + warn!("The tx frame had the following error: {}", e); + VIRTIO_CAN_RESULT_NOT_OK + } + }; + + // If the device cannot send the frame either because socket does not + // exist or the writing the frame fails for another unknown reason + // then behave as receiving a busoff error. + if _response == VIRTIO_CAN_RESULT_NOT_OK { + trace!("Change controller status to STOPPED and busoff to true"); + // TODO: Trigger a config_change notification + self.controller.write().unwrap().config.status = + VIRTIO_CAN_S_CTRL_BUSOFF.into(); + self.controller.write().unwrap().ctrl_state = CAN_CS_STOPPED; + } + + _response + }; + + writer + .write_obj(response) + .map_err(|_| Error::DescriptorWriteFailed)?; + + if vring + .add_used(desc_chain.head_index(), writer.bytes_written() as u32) + .is_err() + { + warn!("Couldn't return used descriptors to the ring"); + } + } + + Ok(true) + } + + pub fn process_rx_requests( + &mut self, + requests: Vec<CanDescriptorChain>, + vring: &VringRwLock, + ) -> Result<bool> { + if requests.is_empty() { + return Ok(true); + } + + // This flag, if true, requests a new tx buffer from the front-end + let mut req_rx_buf = false; + + for desc_chain in requests { + let atomic_mem = self.mem.as_ref().unwrap().memory(); + let mut writer = desc_chain + .clone() + .writer(&atomic_mem) + .map_err(|_| Error::DescriptorWriteFailed)?; + + let response = match self.controller.write().unwrap().pop() { + Ok(item) => { + // If one or more frames have been received then request + // a new rx buffer. + req_rx_buf = true; + item + } + Err(QueueEmpty) => { + return Ok(req_rx_buf); + } + Err(_) => { + return Err(Error::HandleEventUnknown); + } + }; + + let can_rx = match self.check_rx_frame(response) { + Ok(frame) => frame, + Err(e) => { + warn!("The tx frame had the following error: {}", e); + return Err(e); + } + }; + + writer + .write_obj(can_rx) + .map_err(|_| Error::DescriptorWriteFailed)?; + + if vring + .add_used(desc_chain.head_index(), writer.bytes_written() as u32) + .is_err() + { + warn!("Couldn't return used descriptors to the ring"); + } + } + + Ok(true) + } + + /// Process the messages in the vring and dispatch replies + fn process_ctrl_queue(&mut self, vring: &VringRwLock) -> Result<()> { + let requests: Vec<CanDescriptorChain> = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| Error::DescriptorNotFound)? + .collect(); + + if self.process_ctrl_requests(requests, vring)? { + // Send notification once all the requests are processed + vring + .signal_used_queue() + .map_err(|_| Error::NotificationFailed)?; + } + Ok(()) + } + + /// Process the messages in the vring and dispatch replies + fn process_tx_queue(&self, vring: &VringRwLock) -> Result<()> { + let requests: Vec<CanDescriptorChain> = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| Error::DescriptorNotFound)? + .collect(); + + if self.process_tx_requests(requests, vring)? { + // Send notification once all the requests are processed + vring + .signal_used_queue() + .map_err(|_| Error::NotificationFailed)?; + } + + Ok(()) + } + + /// Process the messages in the vring and dispatch replies + fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> { + let requests: Vec<CanDescriptorChain> = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| Error::DescriptorNotFound)? + .collect(); + + if self.process_rx_requests(requests, vring)? { + // Send notification once all the requests are processed + vring + .signal_used_queue() + .map_err(|_| Error::NotificationFailed)?; + } + Ok(()) + } + + /// Set self's VringWorker. + pub(crate) fn set_vring_worker( + &self, + vring_worker: &Arc<VringEpollHandler<Arc<RwLock<VhostUserCanBackend>>>>, + ) { + let rx_event_fd = self.controller.read().unwrap().rx_event_fd.as_raw_fd(); + vring_worker + .register_listener(rx_event_fd, EventSet::IN, u64::from(BACKEND_EFD)) + .expect("Fail to register new handler"); + } +} + +/// VhostUserBackendMut trait methods +impl VhostUserBackendMut for VhostUserCanBackend { + type Vring = VringRwLock; + type Bitmap = (); + + fn num_queues(&self) -> usize { + NUM_QUEUES + } + + fn max_queue_size(&self) -> usize { + QUEUE_SIZE + } + + fn features(&self) -> u64 { + 1 << VIRTIO_F_VERSION_1 + | 1 << VIRTIO_F_NOTIFY_ON_EMPTY + | 1 << VIRTIO_RING_F_EVENT_IDX + | 1 << VIRTIO_CAN_F_CAN_CLASSIC + | 1 << VIRTIO_CAN_F_CAN_FD + | 1 << VIRTIO_RING_F_INDIRECT_DESC + | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() + } + + fn acked_features(&mut self, _features: u64) { + self.acked_features = _features; + } + + fn protocol_features(&self) -> VhostUserProtocolFeatures { + VhostUserProtocolFeatures::MQ + | VhostUserProtocolFeatures::CONFIG + | VhostUserProtocolFeatures::REPLY_ACK + } + + fn get_config(&self, offset: u32, size: u32) -> Vec<u8> { + // SAFETY: The layout of the structure is fixed and can be initialized by + // reading its content from byte array. + unsafe { + from_raw_parts( + self.controller + .write() + .unwrap() + .config() + .as_slice() + .as_ptr() + .offset(offset as isize) as *const _ as *const _, + size as usize, + ) + .to_vec() + } + } + + fn set_event_idx(&mut self, enabled: bool) { + self.event_idx = enabled; + } + + fn update_memory(&mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>) -> IoResult<()> { + self.mem = Some(mem); + Ok(()) + } + + fn handle_event( + &mut self, + device_event: u16, + evset: EventSet, + vrings: &[VringRwLock], + _thread_id: usize, + ) -> IoResult<()> { + if evset != EventSet::IN { + return Err(Error::HandleEventNotEpollIn.into()); + } + + // If the device is in STOPPED state only TX and CTRL messages can be handled + if (self.controller.read().unwrap().ctrl_state == CAN_CS_STOPPED) + && ((device_event == RX_QUEUE) || (device_event == BACKEND_EFD)) + { + trace!("Device is stopped!"); + if device_event == BACKEND_EFD { + let _ = self.controller.write().unwrap().rx_event_fd.read(); + } + return Ok(()); + } + + if device_event == RX_QUEUE && self.controller.write().unwrap().rx_is_empty() { + return Ok(()); + }; + + let vring = if device_event != BACKEND_EFD { + &vrings[device_event as usize] + } else { + let _ = self.controller.write().unwrap().rx_event_fd.read(); + &vrings[RX_QUEUE as usize] + }; + + if self.event_idx { + // vm-virtio's Queue implementation only checks avail_index + // once, so to properly support EVENT_IDX we need to keep + // calling process_request_queue() until it stops finding + // new requests on the queue. + loop { + vring.disable_notification().unwrap(); + match device_event { + CTRL_QUEUE => self.process_ctrl_queue(vring), + TX_QUEUE => self.process_tx_queue(vring), + RX_QUEUE => self.process_rx_queue(vring), + BACKEND_EFD => self.process_rx_queue(vring), + _ => Err(Error::HandleEventUnknown), + }?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + // Without EVENT_IDX, a single call is enough. + match device_event { + CTRL_QUEUE => self.process_ctrl_queue(vring), + TX_QUEUE => self.process_tx_queue(vring), + RX_QUEUE => self.process_rx_queue(vring), + BACKEND_EFD => self.process_rx_queue(vring), + _ => Err(Error::HandleEventUnknown), + }?; + } + Ok(()) + } + + fn exit_event(&self, _thread_index: usize) -> Option<EventFd> { + self.exit_event.try_clone().ok() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::virtio_can::{VirtioCanCtrlResponse, VirtioCanTxResponse}; + use std::mem::size_of; + use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; + use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue}; + use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, Le16, Le32}; + + #[test] + fn test_virtio_can_tx_response_default() { + let response = VirtioCanTxResponse::default(); + assert_eq!(response.result, 0); + } + + #[test] + fn test_virtio_can_ctrl_request_default() { + let request = VirtioCanCtrlRequest::default(); + assert_eq!(request.msg_type, Le16::default()); + } + + #[test] + fn test_virtio_can_ctrl_response_default() { + let response = VirtioCanCtrlResponse::default(); + assert_eq!(response.result, 0); + } + + #[test] + fn test_virtio_can_frame_default() { + let frame = VirtioCanFrame::default(); + assert_eq!(frame.msg_type, Le16::default()); + assert_eq!(frame.length, Le16::default()); + assert_eq!(frame.reserved, Le32::default()); + assert_eq!(frame.flags, Le32::default()); + assert_eq!(frame.can_id, Le32::default()); + assert_eq!(frame.sdu, [0; 64]); + } + + #[test] + fn test_virtio_can_empty_requests() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + let vring = VringRwLock::new(mem.clone(), 0x1000).unwrap(); + + // Empty descriptor chain should be ignored + assert!(vu_can_backend + .process_rx_requests(Vec::<CanDescriptorChain>::new(), &vring) + .expect("Fail to examin empty rx vring")); + assert!(vu_can_backend + .process_tx_requests(Vec::<CanDescriptorChain>::new(), &vring) + .expect("Fail to examin empty tx vring")); + assert!(vu_can_backend + .process_ctrl_requests(Vec::<CanDescriptorChain>::new(), &vring) + .expect("Fail to examin empty ctrl vring")); + } + + #[test] + fn test_virtio_can_empty_handle_request() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + vu_can_backend.update_memory(mem.clone()).unwrap(); + + let vring = VringRwLock::new(mem, 0x1000).unwrap(); + vring.set_queue_info(0x100, 0x200, 0x300).unwrap(); + vring.set_queue_ready(true); + let list_vrings = [vring.clone(), vring.clone(), vring.clone(), vring.clone()]; + + vu_can_backend + .handle_event(RX_QUEUE, EventSet::IN, &list_vrings, 0) + .unwrap(); + + vu_can_backend + .handle_event(TX_QUEUE, EventSet::IN, &list_vrings, 0) + .unwrap(); + + vu_can_backend + .handle_event(CTRL_QUEUE, EventSet::IN, &list_vrings, 0) + .unwrap(); + + vu_can_backend + .handle_event(BACKEND_EFD, EventSet::IN, &list_vrings, 0) + .unwrap(); + } + + fn build_desc_chain_mem( + mem: &GuestMemoryMmap, + count: u16, + flags: Vec<u16>, + len: u32, + ) -> CanDescriptorChain { + let vq = MockSplitQueue::new(mem, 16); + let mut desc_vec = Vec::new(); + + //Create a descriptor chain with count descriptors. + for i in 0..count { + let desc_flags = if i < count - 1 { + flags[i as usize] | VRING_DESC_F_NEXT as u16 + } else { + flags[i as usize] & !VRING_DESC_F_NEXT as u16 + }; + + let desc = Descriptor::new((0x100 * (i + 1)) as u64, len, desc_flags, i + 1); + desc_vec.push(desc); + } + + vq.add_desc_chains(&desc_vec, 0).unwrap(); + + // Create descriptor chain from pre-filled memory + vq.create_queue::<Queue>() + .unwrap() + .iter(GuestMemoryAtomic::new(mem.clone()).memory()) + .unwrap() + .next() + .unwrap() + } + + #[test] + fn test_virtio_can_ctrl_request() { + // Initialize can device and vhost-device-can backend + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + + // Test 1: Since we provide only one write only, an error will be trigger + // the reader will have 0 data. + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem( + &mem, + 1, + vec![VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert_eq!( + vu_can_backend + .process_ctrl_requests(vec![desc_chain], &vring) + .unwrap_err(), + Error::DescriptorReadFailed + ); + + // Test 2: Two write only descriptors are available, so the reader + // will complain for having 0 data + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![VRING_DESC_F_WRITE as u16, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert_eq!( + vu_can_backend + .process_ctrl_requests(vec![desc_chain], &vring) + .unwrap_err(), + Error::DescriptorReadFailed + ); + + // Test 3: The reader descriptor has two bytes of data + // but are initialized to 0 -> Unknown control message + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert!(vu_can_backend + .process_ctrl_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res); + + // Test 4: Successfull test for VIRTIO_CAN_SET_CTRL_MODE_START + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START; + desc_chain + .memory() + .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + // Write a value on the writer which does not represent known messages + desc_chain + .memory() + .write_obj(5, vm_memory::GuestAddress(0x200_u64)) + .unwrap(); + + assert!(vu_can_backend + .process_ctrl_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res); + + // Test 5: Successfull test for VIRTIO_CAN_SET_CTRL_MODE_STOP + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_STOP; + desc_chain + .memory() + .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + // Write a value on the writer which does not represent known messages + desc_chain + .memory() + .write_obj(5, vm_memory::GuestAddress(0x200_u64)) + .unwrap(); + + assert!(vu_can_backend + .process_ctrl_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res); + + // Test 6: Since we provide only one read only, an error will be trigger + // about the writer having 0 data + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], can_mes_len.try_into().unwrap()); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START; + desc_chain + .memory() + .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + assert_eq!( + vu_can_backend + .process_ctrl_requests(vec![desc_chain], &vring) + .unwrap_err(), + Error::DescriptorWriteFailed + ); + + // Test 7: Two read only descriptors are available, so the writer + // will complain for having 0 data + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, 0], can_mes_len.try_into().unwrap()); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_STOP; + desc_chain + .memory() + .write_obj(ctrl_msg, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + assert_eq!( + vu_can_backend + .process_ctrl_requests(vec![desc_chain], &vring) + .unwrap_err(), + Error::DescriptorWriteFailed + ); + + // Test 8: If we interchange the write read descriptors then + // it will fail! + + let can_mes_len = size_of::<VirtioCanCtrlRequest>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![VRING_DESC_F_WRITE as u16, 0], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let ctrl_msg: u16 = VIRTIO_CAN_SET_CTRL_MODE_START; + desc_chain + .memory() + .write_obj(ctrl_msg, vm_memory::GuestAddress(0x200_u64)) + .unwrap(); + + // Write a value on the writer which does not represent known messages + desc_chain + .memory() + .write_obj(5, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + assert!(vu_can_backend + .process_ctrl_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x100_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_OK, can_frame_res); + } + + #[test] + fn test_virtio_can_check_tx_unknown_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 0.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedCanMsgType(0) + ); + } + + #[test] + fn test_virtio_can_check_tx_can_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // Test 1: UnexpectedClassicFlag + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 4.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedClassicFlag + ); + + // Test 2: Return the same length + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap().length, + frame.length + ); + + // Test 3: Return Error::InvalidCanLength(frame_length) when length is out-of-range (>8) + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 40.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::InvalidCanLength(40) + ); + } + + #[test] + fn test_virtio_can_check_tx_canfd_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller).expect("Could not build vhucan device"); + + // Enable VIRTIO_CAN_F_CAN_FD feature + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_FD); + + // Test 1: If no VIRTIO_CAN_FLAGS_FD in flag return UnexpectedClassicFlag + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 12.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedClassicFlag + ); + + // Test 2: If VIRTIO_CAN_FLAGS_FD is in flag check if return message has + // CAN_FRMF_TYPE_FD in flags. + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 12.into(), + reserved: 0.into(), + flags: VIRTIO_CAN_FLAGS_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend + .check_tx_frame(frame) + .unwrap() + .flags + .to_native() + & CAN_FRMF_TYPE_FD, + CAN_FRMF_TYPE_FD + ); + + // Test 3: If 1 is not a valid CAN flag return InvalidCanFlags(1). + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 1.into(), + length: 12.into(), + reserved: 0.into(), + flags: 0x1.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::InvalidCanFlags(1) + ); + + // Test 4: receive frame with the same length if length is < 64 but not + // one of [12, 16, 20, 24, 32, 48, 64] -> InvalidCanLength(40)] + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 40.into(), + reserved: 0.into(), + flags: VIRTIO_CAN_FLAGS_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::InvalidCanLength(40) + ); + + // Test 5: receive frame with length is > 64 + // then -> InvalidCanLength(frame_length). + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 80.into(), + reserved: 0.into(), + flags: VIRTIO_CAN_FLAGS_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::InvalidCanLength(80) + ); + + // Test 6: receive frame with length in [12, 16, 20, 24, 32, 48, 64] + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 48.into(), + reserved: 0.into(), + flags: VIRTIO_CAN_FLAGS_FD.into(), + sdu: [0; 64], + }; + + assert_eq!(vu_can_backend.check_tx_frame(frame).unwrap().length, 48); + } + + #[test] + fn test_virtio_can_check_tx_rtr_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // Test 1: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC and + // VIRTIO_CAN_F_RTR_FRAMES are not negotiated. + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 8.into(), + reserved: 0.into(), + flags: VIRTIO_CAN_FLAGS_RTR.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedRtrFlag + ); + + // Test 2: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated. + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_RTR_FRAMES); + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedRtrFlag + ); + + // Test 3: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should succeed because VIRTIO_CAN_F_CAN_CLASSIC is negotiated. + vu_can_backend + .acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_CLASSIC)); + + assert_eq!( + vu_can_backend + .check_tx_frame(frame) + .unwrap() + .can_id + .to_native() + & CAN_RTR_FLAG, + CAN_RTR_FLAG + ); + + // Test 4: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated, + // and RTR does not work with VIRTIO_CAN_F_CAN_FD. + vu_can_backend.acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_FD)); + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedRtrFlag + ); + } + + #[test] + fn test_virtio_can_check_tx_eff_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // This test is valid for both CAN & CANFD messages, so for simplicity + // we will check only CAN case. + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + // Test 1: Received message should not have CAN_EFF_FLAG in can_id + let mut frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend + .check_tx_frame(frame) + .unwrap() + .can_id + .to_native() + & CAN_EFF_FLAG, + 0 + ); + + // Test 2: If VIRTIO_CAN_FLAGS_EXTENDED is NOT enabled, CAN ID should + // be smaller than 11 bits + frame.can_id = (CAN_SFF_MASK + 1).into(); // CAN_SFF_MASK = 0x7FFU + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedCanId(11) + ); + + // Test 3: Received message should have CAN_EFF_MASK in can_id + frame.flags = VIRTIO_CAN_FLAGS_EXTENDED.into(); + assert_eq!( + vu_can_backend + .check_tx_frame(frame) + .unwrap() + .can_id + .to_native() + & CAN_EFF_FLAG, + CAN_EFF_FLAG + ); + + // Test 4: If VIRTIO_CAN_FLAGS_EXTENDED is enabled, CAN ID should be + // smaller than 29 bits + frame.can_id = (CAN_EFF_MASK + 1).into(); // CAN_EFF_MASK = 0x1FFFFFFFU + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::UnexpectedCanId(29) + ); + } + + #[test] + fn test_virtio_can_tx_general_tests() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + + // Any number of descriptor different than 2 will generate an error + let count = 1; + + // Test 1: Provide only write only descriptor -> Fail + let desc_chain = build_desc_chain_mem(&mem, count, vec![VRING_DESC_F_WRITE as u16], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert_eq!( + vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorReadFailed + ); + + // Test 2: Provide only read only descriptor -> Fail + let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert_eq!( + vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorWriteFailed + ); + + // Test 3: Provide only write only descriptors -> Fail + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![VRING_DESC_F_WRITE as u16, VRING_DESC_F_WRITE as u16], + 0x200, + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert_eq!( + vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorReadFailed + ); + + // Test 4: Provide only read only descriptors -> Fail + let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, 0], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + assert_eq!( + vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorWriteFailed + ); + + // Test 5: Provide correct descriptors but 0 size -> Fail + let desc_chain = build_desc_chain_mem(&mem, 2, vec![0, VRING_DESC_F_WRITE as u16], 0x0); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + assert_eq!( + vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorReadFailed + ); + + // Test 6: Provide correct descriptors and size + let can_mes_len = size_of::<VirtioCanFrame>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + assert!(vu_can_backend + .process_tx_requests(vec![desc_chain], &vring) + .unwrap()); + } + + #[test] + fn test_virtio_can_tx_device_stopped_test() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + + // Enable VIRTIO_CAN_F_CAN_CLASSIC feature + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + let can_mes_len = size_of::<VirtioCanFrame>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 123.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + desc_chain + .memory() + .write_obj(frame, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + desc_chain + .memory() + .write_obj(5, vm_memory::GuestAddress(0x200_u64)) + .unwrap(); + + assert!(vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res); + } + + #[test] + fn test_virtio_can_tx_device_started_test_send_fail() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + + // Enable VIRTIO_CAN_F_CAN_CLASSIC feature + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + // Start the device + controller.write().unwrap().ctrl_state = CAN_CS_STARTED; + + let can_mes_len = size_of::<VirtioCanFrame>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 123.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + desc_chain + .memory() + .write_obj(frame, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + desc_chain + .memory() + .write_obj(5, vm_memory::GuestAddress(0x200_u64)) + .unwrap(); + + assert!(vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res); + } + + #[test] + fn test_virtio_can_tx_device_started_check_frame_fail() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + + // Start the device + controller.write().unwrap().ctrl_state = CAN_CS_STARTED; + + let can_mes_len = size_of::<VirtioCanFrame>(); + let desc_chain = build_desc_chain_mem( + &mem, + 2, + vec![0, VRING_DESC_F_WRITE as u16], + can_mes_len.try_into().unwrap(), + ); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 123.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + desc_chain + .memory() + .write_obj(frame, vm_memory::GuestAddress(0x100_u64)) + .unwrap(); + + desc_chain + .memory() + .write_obj(5, vm_memory::GuestAddress(0x200_u64)) + .unwrap(); + + assert!(vu_can_backend + .process_tx_requests(vec![desc_chain.clone()], &vring) + .unwrap()); + + let can_frame_res = desc_chain + .memory() + .read_obj::<u8>(vm_memory::GuestAddress(0x200_u64)) + .map_err(|_| Error::DescriptorReadFailed) + .unwrap(); + + assert_eq!(VIRTIO_CAN_RESULT_NOT_OK, can_frame_res); + } + + #[test] + fn test_virtio_can_check_rx_err_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: CAN_ERR_FLAG.into(), + length: 0.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::RxFrameUnknownFail + ); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: (CAN_ERR_FLAG | CAN_ERR_BUSOFF).into(), + length: 0.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::BusoffRxFrame + ); + + assert_eq!( + controller.read().unwrap().config.status.to_native(), + VIRTIO_CAN_S_CTRL_BUSOFF + ); + assert_eq!(controller.read().unwrap().ctrl_state, CAN_CS_STOPPED); + } + + #[test] + fn test_virtio_can_check_rx_features_not_negotiated() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 0.into(), + reserved: 0.into(), + flags: CAN_FRMF_TYPE_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::UnexpectedFdFlag + ); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 0.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::UnexpectedClassicFlag + ); + } + + #[test] + fn test_virtio_can_check_rx_eff_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // This test is valid for both CAN & CANFD messages, so for simplicity + // we will check only CAN case. + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + // Test 1: Received message should not have CAN_EFF_FLAG in can_id + let mut frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .can_id + .to_native() + & CAN_EFF_FLAG, + 0 + ); + + // Test 2: Received message's can_id should be smaller than CAN_SFF_MASK + frame.can_id = (CAN_SFF_MASK + 1).into(); // CAN_SFF_MASK = 0x7FFU + assert!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .can_id + .to_native() + < CAN_SFF_MASK, + ); + + // Test 3: Received message should have CAN_EFF_MASK in can_id + frame.can_id = CAN_EFF_FLAG.into(); + assert_eq!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .flags + .to_native() + & VIRTIO_CAN_FLAGS_EXTENDED, + VIRTIO_CAN_FLAGS_EXTENDED + ); + + // Test 4: Received message's can_id should be equal to CAN_EFF_MASK, + // since the 3 MSB have been zeroed. + frame.can_id = (frame.can_id.to_native() | CAN_EFF_MASK).into(); // CAN_EFF_MASK = 0x1FFFFFFFU + assert!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .can_id + .to_native() + == CAN_EFF_MASK + ); + } + + #[test] + fn test_virtio_can_check_rx_rtr_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: CAN_RTR_FLAG.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + // Test 1: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated. + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::UnexpectedRtrFlag + ); + + // Test 2: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should succeed because VIRTIO_CAN_F_CAN_CLASSIC is negotiated. + vu_can_backend + .acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_CLASSIC)); + + assert_eq!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .flags + .to_native() + & VIRTIO_CAN_FLAGS_RTR, + VIRTIO_CAN_FLAGS_RTR + ); + + // Test 3: Take a valid CAN / CANFD message and try to enable RTR in flags. + // the test should fail because VIRTIO_CAN_F_CAN_CLASSIC is not negotiated, + // and RTR does not work with VIRTIO_CAN_F_CAN_FD. + + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: CAN_RTR_FLAG.into(), + length: 8.into(), + reserved: 0.into(), + flags: CAN_FRMF_TYPE_FD.into(), // Mark it as CAN FD frame + sdu: [0; 64], + }; + + vu_can_backend.acked_features((1 << VIRTIO_CAN_F_RTR_FRAMES) | (1 << VIRTIO_CAN_F_CAN_FD)); + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::UnexpectedRtrFlag + ); + } + + #[test] + fn test_virtio_can_check_rx_can_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // Test 1: UnexpectedClassicFlag + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 4.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::UnexpectedClassicFlag + ); + + // Test 2: Return the same length + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap().length, + frame.length + ); + + // Test 3: Return the length equal to 8 + let frame = VirtioCanFrame { + msg_type: VIRTIO_CAN_TX.into(), + can_id: 0.into(), + length: 40.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_tx_frame(frame).unwrap_err(), + Error::InvalidCanLength(40) + ); + } + + #[test] + fn test_virtio_can_check_rx_canfd_frame() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // Enable VIRTIO_CAN_F_CAN_FD feature + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_FD); + + // Test 1: If no CAN_FRMF_TYPE_FD in flags return UnexpectedClassicFlag + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 40.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::UnexpectedClassicFlag + ); + + // Test 2: If VIRTIO_CAN_FLAGS_FD is in flag check if return message has + // VIRTIO_CAN_FLAGS_FD in flags. + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 48.into(), + reserved: 0.into(), + flags: CAN_FRMF_TYPE_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .flags + .to_native() + & VIRTIO_CAN_FLAGS_FD, + VIRTIO_CAN_FLAGS_FD + ); + + // Test 3: receive frame with the same length if length is < 64 + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap().length, + frame.length + ); + + // Test 4: receive frame with length out-of-range: length is > 64 + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 80.into(), + reserved: 0.into(), + flags: CAN_FRMF_TYPE_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::InvalidCanLength(80) + ); + + // Test 5: receive frame with length out-of-range: length no in + // list: [12, 16, 20, 24, 32, 48, 64] + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 62.into(), + reserved: 0.into(), + flags: CAN_FRMF_TYPE_FD.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap_err(), + Error::InvalidCanLength(62) + ); + } + + #[test] + fn test_virtio_can_check_rx_canfd_vcan0() { + let controller = + CanController::new("vcan0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + + // Enable VIRTIO_CAN_F_CAN_FD feature + vu_can_backend.acked_features((1 << VIRTIO_CAN_F_CAN_FD) | (1 << VIRTIO_CAN_F_CAN_CLASSIC)); + + // If VIRTIO_CAN_F_CAN_FD and VIRTIO_CAN_F_CAN_CLASSIC are negotiated + // and interface is "vcan0" check if return message has + // VIRTIO_CAN_FLAGS_FD in flags and has been treated as CANFD frame. + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 0.into(), + length: 48.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + assert_eq!( + vu_can_backend + .check_rx_frame(frame) + .unwrap() + .flags + .to_native() + & VIRTIO_CAN_FLAGS_FD, + VIRTIO_CAN_FLAGS_FD + ); + + assert_eq!( + vu_can_backend.check_rx_frame(frame).unwrap().length, + frame.length + ); + } + + #[test] + fn test_virtio_can_rx_request() { + let controller = + CanController::new("can0".to_string()).expect("Could not build controller"); + let controller = Arc::new(RwLock::new(controller)); + let mut vu_can_backend = + VhostUserCanBackend::new(controller.clone()).expect("Could not build vhucan device"); + let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + + // Note: The following test are focusing only to simple CAN messages. + + // Test 1: This should succeed because there is no element inserted in the + // CAN/FD frames' queue + let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + assert!(!vu_can_backend + .process_rx_requests(vec![desc_chain], &vring) + .unwrap(),); + + // Test 2: This should fail because there is a simple CAN frame + // inserted in the CAN/FD frames' queue, but this does not + // pass the checks. + + // Push a new can message into the can.rs queue + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 123.into(), + length: 64.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + controller.write().unwrap().push(frame).unwrap(); + + let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + assert_eq!( + vu_can_backend + .process_rx_requests(vec![desc_chain], &vring) + .unwrap_err(), + Error::UnexpectedClassicFlag + ); + + // Test 3: This should succeed because there is a simple CAN frame + // inserted in the CAN/FD frames' queue, and this does + // pass the checks. + + // Enable VIRTIO_CAN_F_CAN_CLASSIC feature + vu_can_backend.acked_features(1 << VIRTIO_CAN_F_CAN_CLASSIC); + + // Push a new can message into the can.rs queue + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 123.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + + controller.write().unwrap().push(frame).unwrap(); + + let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + assert!(vu_can_backend + .process_rx_requests(vec![desc_chain], &vring) + .unwrap()); + + // Test 4: This should fail because the descriptor is read-only + let desc_chain = build_desc_chain_mem(&mem, 1, vec![0], 0x200); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + // Push a new can message into the can.rs queue + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 123.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + controller.write().unwrap().push(frame).unwrap(); + + assert_eq!( + vu_can_backend + .process_rx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorWriteFailed + ); + + // Test 5: This should fail because the descriptor length is less + // than VirtioCanFrame size. + let desc_chain = build_desc_chain_mem(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x10); + let mem1 = GuestMemoryAtomic::new(mem.clone()); + let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap(); + vu_can_backend.update_memory(mem1).unwrap(); + + // Push a new can message into the can.rs queue + let frame = VirtioCanFrame { + msg_type: 0.into(), + can_id: 123.into(), + length: 8.into(), + reserved: 0.into(), + flags: 0.into(), + sdu: [0; 64], + }; + controller.write().unwrap().push(frame).unwrap(); + + assert_eq!( + vu_can_backend + .process_rx_requests(vec![desc_chain.clone()], &vring) + .unwrap_err(), + Error::DescriptorWriteFailed + ); + } +} diff --git a/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs new file mode 100755 index 00000000..768dc12a --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/virtio_can.rs @@ -0,0 +1,144 @@ +// CAN virtio bindings +// +// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved. +// Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use vm_memory::{ByteValued, Le16, Le32}; + +/// CAN FRAME Flags and Masks +pub(crate) const CAN_EFF_FLAG: u32 = 0x80000000; // EFF/SFF is set in the MSB +pub(crate) const CAN_RTR_FLAG: u32 = 0x40000000; // remote transmission request +pub(crate) const CAN_ERR_FLAG: u32 = 0x20000000; // error message frame +pub(crate) const CAN_SFF_MASK: u32 = 0x000007FF; // standard frame format (SFF) +pub(crate) const CAN_EFF_MASK: u32 = 0x1FFFFFFF; // extended frame format (EFF) +#[allow(dead_code)] +pub(crate) const CAN_FRMF_BRS: u32 = 0x01; // bit rate switch (2nd bitrate for data) +#[allow(dead_code)] +pub(crate) const CAN_FRMF_ESI: u32 = 0x02; // error state ind. of transmitting node +pub(crate) const CAN_FRMF_TYPE_FD: u32 = 0x10; // internal bit ind. of CAN FD frame +pub(crate) const CAN_ERR_BUSOFF: u32 = 0x00000040; // bus off + +/// CANFD frame's valid data lengths +pub(crate) const CANFD_VALID_LENGTHS: [u32; 7] = [12, 16, 20, 24, 32, 48, 64]; + +/// CAN controller states +pub(crate) const CAN_CS_STARTED: u8 = 0x01; +pub(crate) const CAN_CS_STOPPED: u8 = 0x02; + +/// CAN flags to determine type of CAN FRAME Id +pub(crate) const VIRTIO_CAN_FLAGS_EXTENDED: u32 = 0x8000; +pub(crate) const VIRTIO_CAN_FLAGS_FD: u32 = 0x4000; +pub(crate) const VIRTIO_CAN_FLAGS_RTR: u32 = 0x2000; + +pub(crate) const VIRTIO_CAN_FLAGS_VALID_MASK: u32 = + VIRTIO_CAN_FLAGS_EXTENDED | VIRTIO_CAN_FLAGS_FD | VIRTIO_CAN_FLAGS_RTR; + +pub(crate) const VIRTIO_CAN_TX: u16 = 0x0001; +pub(crate) const VIRTIO_CAN_RX: u16 = 0x0101; + +/// Feature bit numbers +pub const VIRTIO_CAN_F_CAN_CLASSIC: u16 = 0; +pub const VIRTIO_CAN_F_CAN_FD: u16 = 1; +pub const VIRTIO_CAN_S_CTRL_BUSOFF: u16 = 2; /* Controller BusOff */ +#[allow(dead_code)] +pub const VIRTIO_CAN_F_LATE_TX_ACK: u16 = 2; +pub const VIRTIO_CAN_F_RTR_FRAMES: u16 = 3; + +/// Possible values of the status field +pub const VIRTIO_CAN_RESULT_OK: u8 = 0x0; +pub const VIRTIO_CAN_RESULT_NOT_OK: u8 = 0x1; + +/// CAN Control messages +pub const VIRTIO_CAN_SET_CTRL_MODE_START: u16 = 0x0201; +pub const VIRTIO_CAN_SET_CTRL_MODE_STOP: u16 = 0x0202; + +/// Virtio Can Configuration +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub(crate) struct VirtioCanConfig { + /// CAN controller status + pub(crate) status: Le16, +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanConfig {} + +/// Virtio CAN Request / Response messages +/// +/// The response message is a stream of bytes, where first byte represents the +/// status, and rest is message specific data. + +#[derive(Copy, Clone, Default)] +#[repr(C)] +pub struct VirtioCanTxResponse { + pub result: i8, +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanTxResponse {} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(C)] +pub struct VirtioCanFrame { + pub msg_type: Le16, + pub length: Le16, /* 0..8 CC, 0..64 CANFD, 0..2048 CANXL, 12 bits */ + pub reserved: Le32, /* May be needed in part for CAN XL priority */ + pub flags: Le32, + pub can_id: Le32, + pub sdu: [u8; 64], +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanFrame {} + +impl Default for VirtioCanFrame { + fn default() -> Self { + VirtioCanFrame { + msg_type: Le16::default(), + length: Le16::default(), + reserved: Le32::default(), + flags: Le32::default(), + can_id: Le32::default(), + sdu: [0; 64], // Initialize "sdu" with default value (0 in this case) + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Default)] +#[repr(C)] +pub struct VirtioCanHeader { + pub msg_type: Le16, + pub length: Le16, /* 0..8 CC, 0..64 CANFD, 0..2048 CANXL, 12 bits */ + pub reserved: Le32, /* May be needed in part for CAN XL priority */ + pub flags: Le32, + pub can_id: Le32, +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanHeader {} + +#[derive(Copy, Clone, Default)] +#[repr(C)] +pub struct VirtioCanCtrlRequest { + pub msg_type: Le16, +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanCtrlRequest {} + +#[derive(Copy, Clone, Default)] +#[repr(C)] +pub struct VirtioCanCtrlResponse { + pub result: i8, +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioCanCtrlResponse {} |