Vhost-user-console device and can license fixes 45/29545/2
authorMichele Paolino <m.paolino@virtualopensystems.com>
Mon, 18 Dec 2023 13:48:17 +0000 (13:48 +0000)
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>
Fri, 29 Dec 2023 15:58:49 +0000 (15:58 +0000)
This patch presents a new console device and fixes vhost-user-can
license.
vhost-device can and conosle devices will be proposed to
rust-vmm/vhost-device community, and in case of acceptace this patch
will be updated.

Bug-AGL: SPEC-4834

Change-Id: I4dcded8733195158f848bd0e4fd4f4618a378c3a
Signed-off-by: Michele Paolino <m.paolino@virtualopensystems.com>
16 files changed:
meta-egvirt/conf/include/agl-egvirt.inc
meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE [deleted file]
meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE-APACHE [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc [new file with mode: 0644]
meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb [new file with mode: 0644]

index 40cb4ca..bcc3e1d 100644 (file)
@@ -6,6 +6,7 @@ FEATURE_PACKAGES_virtio-loopback = " \
      vhost-device-rng \
      vhost-device-gpio \
      vhost-device-can \
+     vhost-device-console \
      "
 
 EXTRA_IMAGE_FEATURES += "virtio-loopback"
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE-APACHE b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE-APACHE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -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/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE-BSD-3-Clause
new file mode 100644 (file)
index 0000000..dd975d9
--- /dev/null
@@ -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.
index c18e751..4db8ebe 100644 (file)
@@ -7,9 +7,10 @@ EXTRAPATHS:prepend := "${THISDIR}:"
 
 SRC_URI = " file://. "
 
-LICENSE = "Apache-2.0"
+LICENSE = "Apache-2.0 | BSD-3-Clause"
 LIC_FILES_CHKSUM = "\
-    file://LICENSE;md5=d41d8cd98f00b204e9800998ecf8427e \
+    file://LICENSE-APACHE;md5=3b83ef96387f14655fc854ddc3c6bd57 \
+    file://LICENSE-BSD-3-Clause;md5=2489db1359f496fff34bd393df63947e \
 "
 
 inherit cargo
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md
new file mode 100644 (file)
index 0000000..51d3f04
--- /dev/null
@@ -0,0 +1,15 @@
+# Changelog
+## [Unreleased]
+
+### Added
+
+### Changed
+
+### Fixed
+
+### Deprecated
+
+## [0.1.0]
+
+First release
+
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml
new file mode 100644 (file)
index 0000000..c2f9f71
--- /dev/null
@@ -0,0 +1,35 @@
+[package]
+name = "vhost-device-console"
+version = "0.0.1"
+authors = ["Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>"]
+description = "vhost console backend device"
+repository = "https://github.com/rust-vmm/vhost-device"
+readme = "README.md"
+keywords = ["console", "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]
+console = "0.15.7"
+queues = "1.0.2"
+clap = { version = "4.2.5",  features = ["derive"] }
+env_logger = "0.10"
+libc = "0.2"
+log = "0.4"
+thiserror = "1.0"
+vhost = { version = "0.8", features = ["vhost-user-slave"] }
+vhost-user-backend = "0.10"
+virtio-bindings = "0.2.1"
+virtio-queue = "0.9"
+vm-memory = "0.12"
+vmm-sys-util = "0.11"
+
+[dev-dependencies]
+assert_matches = "1.5"
+virtio-queue = { version = "0.9", features = ["test-utils"] }
+vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] }
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-APACHE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -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-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/LICENSE-BSD-3-Clause
new file mode 100644 (file)
index 0000000..dd975d9
--- /dev/null
@@ -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-console/vhost-device-console-0.1.0/README.md b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md
new file mode 100644 (file)
index 0000000..b2c9244
--- /dev/null
@@ -0,0 +1,28 @@
+# vhost-device-console - Console emulation backend daemon
+
+## Description
+This program is a vhost-user backend that emulates a VirtIO Consolw device.
+
+## Synopsis
+
+## Options
+
+.. program:: vhost-device-console
+
+.. 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.
+
+## Examples
+
+## 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-console/vhost-device-console-0.1.0/src/backend.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs
new file mode 100644 (file)
index 0000000..15b50a3
--- /dev/null
@@ -0,0 +1,211 @@
+// VIRTIO CONSOLE Emulation via vhost-user
+//
+// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+//          Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{error, info, warn};
+use std::process::exit;
+use std::sync::{Arc, RwLock};
+use std::thread::{spawn, JoinHandle};
+
+use clap::Parser;
+use thiserror::Error as ThisError;
+use vhost::{vhost_user, vhost_user::Listener};
+use vhost_user_backend::VhostUserDaemon;
+use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
+
+use crate::console::{ConsoleController};
+use crate::vhu_console::VhostUserConsoleBackend;
+
+pub(crate) type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug, ThisError)]
+/// Errors related to low level Console helpers
+pub(crate) enum Error {
+    #[error("Invalid socket count: {0}")]
+    SocketCountInvalid(usize),
+    #[error("Failed to join threads")]
+    FailedJoiningThreads,
+    #[error("Could not create console controller: {0}")]
+    CouldNotCreateConsoleController(crate::console::Error),
+    #[error("Could not create console backend: {0}")]
+    CouldNotCreateBackend(crate::vhu_console::Error),
+    #[error("Could not create daemon: {0}")]
+    CouldNotCreateDaemon(vhost_user_backend::Error),
+}
+
+#[derive(Parser, Debug)]
+#[clap(author, version, about, long_about = None)]
+struct ConsoleArgs {
+    /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
+    #[clap(short, long)]
+    socket_path: String,
+
+    /// A console device name to be used for reading (ex. vconsole, console0, console1, ... etc.)
+    #[clap(short = 'i', long)]
+    console_path: String,
+
+    /// Number of guests (sockets) to connect to.
+    #[clap(short = 'c', long, default_value_t = 1)]
+    socket_count: u32,
+}
+
+#[derive(PartialEq, Debug)]
+struct ConsoleConfiguration {
+    socket_path: String,
+    socket_count: u32,
+    console_path: String,
+}
+
+impl TryFrom<ConsoleArgs> for ConsoleConfiguration {
+    type Error = Error;
+
+    fn try_from(args: ConsoleArgs) -> Result<Self> {
+
+        if args.socket_count == 0 {
+            return Err(Error::SocketCountInvalid(0));
+        }
+
+               let console_path = args.console_path.trim().to_string();
+
+        Ok(ConsoleConfiguration {
+            socket_path: args.socket_path,
+                       socket_count: args.socket_count,
+            console_path,
+        })
+    }
+}
+
+fn start_backend(args: ConsoleArgs) -> Result<()> {
+
+       println!("start_backend function!\n");
+
+    let config = ConsoleConfiguration::try_from(args).unwrap();
+    let mut handles = Vec::new();
+
+    for _ in 0..config.socket_count {
+        let socket = config.socket_path.to_owned();
+        let console_path = config.console_path.to_owned();
+
+        let handle: JoinHandle<Result<()>> = spawn(move || loop {
+            // A separate thread is spawned for each socket and console connect to a separate guest.
+            // These are run in an infinite loop to not require the daemon to be restarted once a
+            // guest exits.
+            //
+            // There isn't much value in complicating code here to return an error from the
+            // threads, and so the code uses unwrap() instead. The panic on a thread won't cause
+            // trouble to other threads/guests or the main() function and should be safe for the
+            // daemon.
+
+            let controller =
+                ConsoleController::new(console_path.clone()).map_err(Error::CouldNotCreateConsoleController)?;
+                       let arc_controller =  Arc::new(RwLock::new(controller));
+            let vu_console_backend = Arc::new(RwLock::new(
+                VhostUserConsoleBackend::new(arc_controller).map_err(Error::CouldNotCreateBackend)?,
+            ));
+
+            let mut daemon = VhostUserDaemon::new(
+                String::from("vhost-device-console-backend"),
+                vu_console_backend.clone(),
+                GuestMemoryAtomic::new(GuestMemoryMmap::new()),
+            )
+            .map_err(Error::CouldNotCreateDaemon)?;
+
+            /* Start the read thread -- need to handle it after termination */
+            let vring_workers = daemon.get_epoll_handlers();
+            vu_console_backend.read()
+                          .unwrap()
+                          .set_vring_worker(&vring_workers[0]);
+                       VhostUserConsoleBackend::start_console_thread(&vu_console_backend);
+
+            let listener = Listener::new(socket.clone(), true).unwrap();
+            daemon.start(listener).unwrap();
+
+            match daemon.wait() {
+                Ok(()) => {
+                    info!("Stopping cleanly.");
+                }
+                Err(vhost_user_backend::Error::HandleRequest(
+                    vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected,
+                )) => {
+                    info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
+                }
+                Err(e) => {
+                    warn!("Error running daemon: {:?}", e);
+                }
+            }
+
+            // No matter the result, we need to shut down the worker thread.
+            vu_console_backend.read().unwrap().exit_event.write(1).unwrap();
+        });
+
+        handles.push(handle);
+    }
+
+    for handle in handles {
+        handle.join().map_err(|_| Error::FailedJoiningThreads)??;
+    }
+
+    Ok(())
+}
+
+pub(crate) fn console_init() {
+    env_logger::init();
+       println!("Console_init function!");
+    if let Err(e) = start_backend(ConsoleArgs::parse()) {
+        error!("{e}");
+        exit(1);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_console_configuration_try_from_valid_args() {
+        let args = ConsoleArgs {
+            socket_path: String::from("/path/to/socket"),
+            console_path: String::from("vconsole"),
+            socket_count: 3,
+        };
+
+        let result = ConsoleConfiguration::try_from(args);
+
+        assert!(result.is_ok());
+
+        let config = result.unwrap();
+        assert_eq!(config.socket_path, "/path/to/socket");
+        assert_eq!(config.console_path, "vconsole");
+        assert_eq!(config.socket_count, 3);
+    }
+
+    #[test]
+    fn test_console_configuration_try_from_invalid_args() {
+        // Test with socket_count = 0
+        let args_invalid_count = ConsoleArgs {
+            socket_path: String::from("/path/to/socket"),
+            console_path: String::from("vconsole"),
+            socket_count: 0,
+        };
+
+        let result_invalid_count = ConsoleConfiguration::try_from(args_invalid_count);
+        assert!(result_invalid_count.is_err());
+    }
+
+    #[test]
+    fn test_start_backend_success() {
+        // Test start_backend with valid arguments
+        let args = ConsoleArgs {
+            socket_path: String::from("/path/to/socket"),
+            console_path: String::from("vconsole"),
+            socket_count: 2,
+        };
+
+        let result = start_backend(args);
+
+        assert!(result.is_ok());
+    }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs
new file mode 100644 (file)
index 0000000..2e2972b
--- /dev/null
@@ -0,0 +1,86 @@
+// CAN backend device
+//
+// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+//          Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{warn, error};
+use std::sync::{Arc, RwLock};
+
+use thiserror::Error as ThisError;
+use vm_memory::{ByteValued, Le16};
+
+use crate::vhu_console::{VirtioConsoleConfig, VirtioConsoleControl};
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
+pub(crate) enum Error {
+    #[error("Console not enabled yet")]
+    ConsoleNotEnabled,
+}
+
+#[derive(Debug)]
+pub(crate) struct ConsoleController {
+    config: VirtioConsoleConfig,
+    pub console_name: String,
+}
+
+impl ConsoleController {
+    // Creates a new controller corresponding to `device`.
+    pub(crate) fn new(console_name: String) -> Result<ConsoleController> {
+
+        let console_name = console_name.to_owned();
+               println!("console_name: {:?}", console_name);
+
+        Ok(ConsoleController {
+            config: VirtioConsoleConfig {
+                                               cols: 20.into(),
+                                               rows: 20.into(),
+                                               max_nr_ports: 1.into(),
+                                               emerg_wr: 64.into(),
+                                       },
+            console_name,
+        })
+    }
+
+    pub(crate) fn config(&self) -> &VirtioConsoleConfig {
+               log::trace!("Get config\n");
+        &self.config
+    }
+
+    pub(crate) fn operation(&self, tx_request: VirtioConsoleControl) -> Result<()> {
+               log::trace!("Console operation\n");
+               Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_new_console_controller() {
+        let console_name = String::from("test_console");
+        let controller = ConsoleController::new(console_name.clone());
+
+        assert!(controller.is_ok());
+
+        let controller = controller.unwrap();
+        assert_eq!(controller.console_name, "test_console");
+    }
+
+    #[test]
+    fn test_console_controller_config() {
+        let console_name = String::from("test_console");
+        let controller = ConsoleController::new(console_name).unwrap();
+
+        let config = controller.config();
+        assert_eq!(config.cols, 20);
+        assert_eq!(config.rows, 20);
+        assert_eq!(config.max_nr_ports, 1);
+        assert_eq!(config.emerg_wr, 64);
+    }
+}
+
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs
new file mode 100644 (file)
index 0000000..cceafb4
--- /dev/null
@@ -0,0 +1,18 @@
+// VIRTIO CONSOLE Emulation via vhost-user
+//
+// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+//          Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+#[cfg(target_env = "gnu")]
+mod backend;
+#[cfg(target_env = "gnu")]
+mod console;
+#[cfg(target_env = "gnu")]
+mod vhu_console;
+
+#[cfg(target_env = "gnu")]
+fn main() {
+    backend::console_init()
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs
new file mode 100644 (file)
index 0000000..ebddb00
--- /dev/null
@@ -0,0 +1,794 @@
+// vhost device console
+//
+// Copyright 2023 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
+//          Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+//
+// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
+
+use log::{warn, error};
+use std::mem::size_of;
+use std::slice::from_raw_parts;
+use std::sync::{Arc, RwLock};
+use std::{
+    convert,
+    io::{self, Result as IoResult},
+};
+use std::io::{Write};
+use std::os::fd::AsRawFd;
+use thiserror::Error as ThisError;
+use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
+use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT};
+use virtio_bindings::bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1};
+use virtio_bindings::bindings::virtio_ring::{
+    VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
+};
+use virtio_queue::{DescriptorChain, QueueOwnedT};
+use vm_memory::{
+    ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard,
+    GuestMemoryMmap, Le16, Le32,
+};
+use vmm_sys_util::epoll::EventSet;
+use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
+use vhost_user_backend::VringEpollHandler;
+use crate::console::{ConsoleController};
+
+use std::thread::{JoinHandle, spawn};
+use queues::{Queue, IsQueue};
+use std::thread;
+use std::time::Duration;
+use console::Term;
+
+/// Feature bit numbers
+pub const VIRTIO_CONSOLE_F_SIZE: u16 = 0;
+pub const VIRTIO_CONSOLE_F_MULTIPORT: u16 = 1;
+pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u16 = 2;
+
+/// Virtio configuration
+const QUEUE_SIZE: usize = 128;
+const NUM_QUEUES: usize = 4;
+
+/// Queues
+const RX_QUEUE: u16 = 0;
+const TX_QUEUE: u16 = 1;
+const CTRL_RX_QUEUE: u16 = 2;
+const CTRL_TX_QUEUE: u16 = 3;
+const BACKEND_EFD: u16 = (NUM_QUEUES + 1) as u16;
+const BACKEND_RX_EFD: u16 = (NUM_QUEUES + 2) as u16;
+
+
+/// Console virtio control messages
+const VIRTIO_CONSOLE_DEVICE_READY: u16 = 0 ;
+const VIRTIO_CONSOLE_PORT_ADD: u16 = 1;
+const VIRTIO_CONSOLE_PORT_REMOVE: u16 = 2; 
+const VIRTIO_CONSOLE_PORT_READY: u16 = 3;
+const VIRTIO_CONSOLE_CONSOLE_PORT: u16 = 4; 
+const VIRTIO_CONSOLE_RESIZE: u16 = 5;
+const VIRTIO_CONSOLE_PORT_OPEN: u16 = 6; 
+const VIRTIO_CONSOLE_PORT_NAME: u16 = 7;
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
+pub(crate) enum Error {
+    #[error("Failed to handle event, didn't match EPOLLIN")]
+    HandleEventNotEpollIn,
+    #[error("Failed to handle unknown event")]
+    HandleEventUnknown,
+    #[error("Received unexpected write only descriptor at index {0}")]
+    UnexpectedWriteOnlyDescriptor(usize),
+    #[error("Received unexpected readable descriptor at index {0}")]
+    UnexpectedReadableDescriptor(usize),
+    #[error("Invalid descriptor count {0}")]
+    UnexpectedDescriptorCount(usize),
+    #[error("Invalid descriptor size, expected: {0}, found: {1}")]
+    UnexpectedDescriptorSize(usize, u32),
+       #[error("Descriptor not found")]
+       DescriptorNotFound,
+       #[error("Failed to send notification")]
+       NotificationFailed,
+    #[error("Descriptor read failed")]
+    DescriptorReadFailed,
+    #[error("Descriptor write failed")]
+    DescriptorWriteFailed,
+    #[error("Failed to create new EventFd")]
+    EventFdFailed,
+    #[error("Failed to remove rx queue")]
+    EmptyQueue,
+}
+
+impl convert::From<Error> for io::Error {
+    fn from(e: Error) -> Self {
+        io::Error::new(io::ErrorKind::Other, e)
+    }
+}
+
+/// Virtio Console Config
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub(crate) struct VirtioConsoleConfig {
+    pub cols: Le16,
+    pub rows: Le16,
+    pub max_nr_ports: Le32,
+    pub emerg_wr: Le32,
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioConsoleConfig {}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub(crate) struct VirtioConsoleControl {
+    pub id: Le32,
+    pub event: Le16,
+    pub value: Le16,
+}
+
+use std::io::Cursor;
+
+impl VirtioConsoleControl {
+    fn to_le_bytes(&self) -> Vec<u8> {
+        let mut buffer = Vec::new();
+
+               buffer.extend_from_slice(&self.id.to_native().to_le_bytes());
+               buffer.extend_from_slice(&self.event.to_native().to_le_bytes());
+               buffer.extend_from_slice(&self.value.to_native().to_le_bytes());
+        buffer
+    }
+}
+
+// SAFETY: The layout of the structure is fixed and can be initialized by
+// reading its content from byte array.
+unsafe impl ByteValued for VirtioConsoleControl {}
+
+pub(crate) struct VhostUserConsoleBackend {
+    controller: Arc<RwLock<ConsoleController>>,
+       acked_features: u64,
+    event_idx: bool,
+       rx_fifo: Queue<VirtioConsoleControl>,
+    pub(crate) ready: bool,
+    pub(crate) ready_to_write: bool,
+       pub(crate) output_buffer: String,
+    pub(crate) rx_event: EventFd,
+    pub(crate) rx_ctrl_event: EventFd,
+    pub(crate) exit_event: EventFd,
+    mem: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
+}
+
+type ConsoleDescriptorChain = DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap<()>>>;
+
+impl VhostUserConsoleBackend {
+    pub(crate) fn new(controller: Arc<RwLock<ConsoleController>>) -> Result<Self> {
+        Ok(VhostUserConsoleBackend {
+            controller: controller,
+            event_idx: false,
+                       rx_fifo: Queue::new(),
+                       acked_features: 0x0,
+                       ready: false,
+                       ready_to_write: false,
+                       output_buffer: String::new(),
+            rx_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
+            rx_ctrl_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
+            exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
+            mem: None,
+        })
+    }
+
+       fn check_features (&self, features: u16) -> bool {
+               (self.acked_features & (1 << features)) != 0
+       }
+
+    fn print_console_frame (&self, control_msg: VirtioConsoleControl) {
+        println!("id 0x{:x}", control_msg.id.to_native());
+        println!("event 0x{:x}", control_msg.event.to_native());
+        println!("value 0x{:x}", control_msg.value.to_native());
+    }
+
+    fn process_rx_requests(
+               &mut self,
+               requests: Vec<ConsoleDescriptorChain>,
+               vring: &VringRwLock
+       ) -> Result<bool> {
+               log::trace!("process_rx_requests");
+
+        if requests.is_empty() {
+                       log::trace!("requests.is_empty");
+                       vring.signal_used_queue();
+            return Ok(true);
+        }
+
+               log::trace!("requests.len: {:?}", requests.len());
+        let desc_chain = &requests[0];
+        let descriptors: Vec<_> = desc_chain.clone().collect();
+
+               log::trace!("descriptors.len(): {:?}", descriptors.len());
+        if descriptors.len() != 1 {
+                       log::trace!("Error::UnexpectedDescriptorCount");
+            return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
+        }
+
+        let desc_request = descriptors[0];
+        if !desc_request.is_write_only() {
+                       log::trace!("!desc_request.is_write_only()");
+            return Err(Error::UnexpectedReadableDescriptor(1));
+        }
+
+        // TODO: if buffer is more than the the desc_request length,
+        //       write the remaining in the next chain.
+               log::trace!("desc_request.len(): {}", desc_request.len());
+               let response = self.output_buffer.clone();
+
+        desc_chain
+            .memory()
+            .write_slice(response.as_bytes(), desc_request.addr())
+            .map_err(|_| Error::DescriptorWriteFailed)?;
+
+        if vring.add_used(desc_chain.head_index(), response.as_bytes().len() as u32).is_err() {
+            warn!("Couldn't return used descriptors to the ring");
+        }
+
+        Ok(true)
+    }
+
+    fn process_tx_requests(
+               &self,
+               requests: Vec<ConsoleDescriptorChain>,
+               vring: &VringRwLock
+       ) -> Result<bool> {
+               log::trace!("process_tx_requests");
+
+        if requests.is_empty() {
+                       log::trace!("requests.is_empty");
+            return Ok(true);
+        }
+
+               log::trace!("requests.len: {:?}", requests.len());
+        for desc_chain in requests {
+            let descriptors: Vec<_> = desc_chain.clone().collect();
+
+                       log::trace!("descriptors.len(): {:?}", descriptors.len());
+            if descriptors.len() != 1 {
+                               log::trace!("Error::UnexpectedDescriptorCount");
+                return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
+            }
+
+            let desc_request = descriptors[0];
+            if desc_request.is_write_only() {
+                               log::trace!("Error::UnexpectedReadOnlyDescriptor");
+                return Err(Error::UnexpectedWriteOnlyDescriptor(0));
+                       }
+
+                       log::trace!("desc_request.len(): {}", desc_request.len());
+                       let desc_len = desc_request.len();
+
+                       let mut buffer = [0 as u8; 4096];
+
+            let request = desc_chain
+                .memory()
+                .read_slice(&mut buffer, desc_request.addr())
+                .map_err(|_| Error::DescriptorReadFailed)?;
+
+                       let new_buff = &buffer[0..desc_len as usize];
+
+                   let my_string = String::from_utf8(new_buff.to_vec()).unwrap();
+                   log::trace!("{}", my_string);
+                   print!("{}", my_string);
+           io::stdout().flush().unwrap(); // Ensure the prompt is displayed.
+
+            if vring.add_used(desc_chain.head_index(), desc_request.len()).is_err() {
+                log::trace!("Couldn't return used descriptors to the ring");
+                warn!("Couldn't return used descriptors to the ring");
+            }
+        }
+
+        Ok(true)
+    }
+
+    fn process_ctrl_rx_requests(
+               &mut self,
+               requests: Vec<ConsoleDescriptorChain>,
+               vring: &VringRwLock,
+       ) -> Result<bool> {
+               log::trace!("process_ctrl_rx_requests");
+
+        if requests.is_empty() {
+                       log::trace!("requests.is_empty()");
+            return Ok(true);
+        }
+               log::trace!("\trequests.len(): {}", requests.len());
+
+        for desc_chain in requests {
+
+                       let descriptors: Vec<_> = desc_chain.clone().collect();
+
+                       log::trace!("descriptors.len(): {:?}", descriptors.len());
+            if descriptors.len() < 1 {
+                               warn!("Error::UnexpectedDescriptorCount");
+                return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
+            }
+
+                       log::trace!("self.rx_fifo.size(): {}", self.rx_fifo.size());
+                       let ctrl_msg: VirtioConsoleControl = match self.rx_fifo.remove() {
+                               Ok(item) => item,         
+                               _ => {
+                                               log::trace!("No rx elements");
+                                               return Ok(false)
+                                        },
+                       };
+
+            let desc_request = descriptors[0];
+            if !desc_request.is_write_only() {
+                               warn!("Error::UnexpectedWriteOnlyDescriptor");
+                return Err(Error::UnexpectedWriteOnlyDescriptor(0));
+            }
+
+            if (desc_request.len() as usize) < size_of::<VirtioConsoleControl>() {
+                               log::trace!("UnexpectedDescriptorSize, len = {:?}", desc_request.len());
+                return Err(Error::UnexpectedDescriptorSize(
+                    size_of::<VirtioConsoleControl>(),
+                    desc_request.len(),
+                ));
+            }
+
+                       log::trace!("desc_request.len(): {}", desc_request.len());
+                       self.print_console_frame(ctrl_msg);
+
+                   let mut buffer: Vec<u8> = Vec::new();
+                   buffer.extend_from_slice(&ctrl_msg.to_le_bytes());
+               
+                       if ctrl_msg.event.to_native() == VIRTIO_CONSOLE_PORT_NAME {
+                       let string_bytes = "org.fedoraproject.console.foo!".as_bytes();
+                       buffer.extend_from_slice(string_bytes);
+                       };
+
+                       desc_chain
+                   .memory()
+                   .write_slice(&buffer, desc_request.addr())
+                   .map_err(|_| Error::DescriptorWriteFailed)?;
+
+            if vring.add_used(desc_chain.head_index(), desc_request.len()).is_err() {
+                log::trace!("Couldn't return used descriptors to the ring");
+                warn!("Couldn't return used descriptors to the ring");
+            }
+        }
+
+        Ok(true)
+    }
+
+       fn handle_control_msg (
+               &mut self,
+               vring: &VringRwLock,
+               ctrl_msg: VirtioConsoleControl
+       ) -> Result<()> {
+
+               let mut ctrl_msg_reply = VirtioConsoleControl {
+                                                               id: 0.into(),
+                                                               event: 0.into(),
+                                                               value: 1.into(),
+                                                       };
+               match ctrl_msg.event.to_native() {
+                       VIRTIO_CONSOLE_DEVICE_READY => {
+                               log::trace!("VIRTIO_CONSOLE_DEVICE_READY");
+                               self.ready = true;
+                               ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_ADD.into();
+                               self.rx_fifo.add(ctrl_msg_reply);
+                               self.process_ctrl_rx_queue(vring)?;
+                       },
+                   VIRTIO_CONSOLE_PORT_READY => {
+                               log::trace!("VIRTIO_CONSOLE_PORT_READY");
+                               ctrl_msg_reply.event = VIRTIO_CONSOLE_CONSOLE_PORT.into();
+                               self.rx_fifo.add(ctrl_msg_reply.clone());
+                               self.process_ctrl_rx_queue(vring)?;
+
+                               ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_NAME.into();
+                               self.rx_fifo.add(ctrl_msg_reply.clone());
+                               self.process_ctrl_rx_queue(vring)?;
+
+                               ctrl_msg_reply.event = VIRTIO_CONSOLE_PORT_OPEN.into();
+                               self.rx_fifo.add(ctrl_msg_reply.clone());
+                               self.process_ctrl_rx_queue(vring)?;
+                       },
+                   VIRTIO_CONSOLE_PORT_OPEN => {
+                               log::trace!("VIRTIO_CONSOLE_PORT_OPEN");
+                       },
+                   _ => {
+                               log::trace!("Uknown control event");
+                               return Err(Error::HandleEventUnknown);
+                       }
+               };
+        Ok(())
+       }
+
+    fn process_ctrl_tx_requests(
+               &mut self,
+               requests: Vec<ConsoleDescriptorChain>,
+               vring: &VringRwLock,
+               rx_ctrl_vring: &VringRwLock
+       ) -> Result<bool> {
+               log::trace!("process_ctrl_tx_requests");
+
+        if requests.is_empty() {
+                       log::trace!("requests.is_empty()");
+            return Ok(true);
+        }
+
+        for desc_chain in requests {
+                       let descriptors: Vec<_> = desc_chain.clone().collect();
+
+            if descriptors.len() < 1 {
+                               warn!("Error::UnexpectedDescriptorCount");
+                return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
+            }
+
+                       log::trace!("descriptors.len(): {:?}", descriptors.len());
+
+            let desc_request = descriptors[0];
+            if desc_request.is_write_only() {
+                               log::trace!("This is write only");
+                return Err(Error::UnexpectedWriteOnlyDescriptor(0));
+            } else {
+                               log::trace!("This is read only");
+                       }
+
+            if desc_request.len() as usize != size_of::<VirtioConsoleControl>() {
+                               log::trace!("UnexpectedDescriptorSize, len = {:?}", desc_request.len());
+                return Err(Error::UnexpectedDescriptorSize(
+                    size_of::<VirtioConsoleControl>(),
+                    desc_request.len(),
+                ));
+            }
+
+                       log::trace!("desc_request.len: {}", desc_request.len());
+
+            let mut request = desc_chain
+                .memory()
+                .read_obj::<VirtioConsoleControl>(desc_request.addr())
+                .map_err(|_| Error::DescriptorReadFailed)?;
+
+                       self.print_console_frame(request);
+
+                       self.handle_control_msg(rx_ctrl_vring, request);
+
+                       if let Some(event_fd) = rx_ctrl_vring.get_ref().get_kick() {
+                               self.rx_ctrl_event.write(1).unwrap();
+                       } else {
+                           // Handle the case where `state` is `None`.
+                           log::trace!("EventFd is not available.");
+                       }
+
+            if vring.add_used(desc_chain.head_index(), desc_request.len()).is_err() {
+                log::trace!("Couldn't return used descriptors to the ring");
+                warn!("Couldn't return used descriptors to the ring");
+            }
+        }
+
+        Ok(true)
+    }
+
+    /// Process the messages in the vring and dispatch replies
+    fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
+               log::trace!("process_rx_queue");
+        let requests: Vec<_> = vring
+            .get_mut()
+            .get_queue_mut()
+            .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
+                       log::trace!("Send notification once all the requests of queue 0 are processed");
+            vring
+                .signal_used_queue()
+                .map_err(|_| {
+                                       log::trace!("NotificationFailed");
+                                       Error::NotificationFailed
+                               })?;
+        }
+               Ok(())
+    }
+
+    /// Process the messages in the vring and dispatch replies
+    fn process_tx_queue(&self, vring: &VringRwLock) -> Result<()> {
+               log::trace!("process_tx_queue");
+        let requests: Vec<_> = vring
+            .get_mut()
+            .get_queue_mut()
+            .iter(self.mem.as_ref().unwrap().memory())
+            .map_err(|_| Error::DescriptorNotFound)?
+            .collect();
+
+        if self.process_tx_requests(requests, vring)? {
+            // Send notification once all the requests are processed
+                       log::trace!("Send notification once all the requests of queue 1 are processed");
+            vring
+                .signal_used_queue()
+                .map_err(|_| {
+                                       log::trace!("signal_used_queue error");
+                                       Error::NotificationFailed
+                               })?;
+        }
+
+        Ok(())
+    }
+
+    /// Process the messages in the vring and dispatch replies
+    fn process_ctrl_rx_queue(&mut self, vring: &VringRwLock) -> Result<()> {
+               log::trace!("process_ctrl_rx_queue");
+        let requests: Vec<_> = vring
+            .get_mut()
+            .get_queue_mut()
+            .iter(self.mem.as_ref().unwrap().memory())
+            .map_err(|_| Error::DescriptorNotFound)?
+            .collect();
+
+        if self.process_ctrl_rx_requests(requests, vring)? {
+                       log::trace!("Send notification once all the requests of queue 2 are processed");
+            // Send notification once all the requests are processed
+            vring
+                .signal_used_queue()
+                .map_err(|_| Error::NotificationFailed)?;
+        }
+               Ok(())
+    }
+
+    /// Process the messages in the vring and dispatch replies
+    fn process_ctrl_tx_queue(&mut self, vring: &VringRwLock, rx_ctrl_vring: &VringRwLock) -> Result<()> {
+               log::trace!("process_ctrl_tx_queue");
+        let requests: Vec<_> = vring
+            .get_mut()
+            .get_queue_mut()
+            .iter(self.mem.as_ref().unwrap().memory())
+            .map_err(|_| Error::DescriptorNotFound)?
+            .collect();
+
+        if self.process_ctrl_tx_requests(requests, vring, rx_ctrl_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<VhostUserConsoleBackend>>, VringRwLock, ()>>,
+    ) {
+               let rx_event_fd = self.rx_event.as_raw_fd();
+               vring_worker
+            .register_listener(
+                               rx_event_fd,
+                               EventSet::IN,
+                               u64::from(BACKEND_EFD))
+            .unwrap();
+
+               let rx_ctrl_event_fd = self.rx_ctrl_event.as_raw_fd();
+               vring_worker
+            .register_listener(
+                               rx_ctrl_event_fd,
+                               EventSet::IN,
+                               u64::from(BACKEND_RX_EFD))
+            .unwrap();
+    }
+
+    /// Start console thread.
+    pub(crate) fn start_console_thread(
+               vhu_console: &Arc<RwLock<VhostUserConsoleBackend>>,
+    ) {
+
+               let vhu_console = Arc::clone(&vhu_console);
+       print!("Enter text and press Enter: ");
+
+               // Spawn a new thread to handle input.
+       spawn( move || {
+                       loop {
+                let ready = vhu_console.read().unwrap().ready_to_write;
+                if ready {
+
+                    let term = Term::stdout();
+                    let character = term.read_char().unwrap();
+                                   log::trace!("You entered: {}", character);
+
+                    // Pass the data to vhu_console and trigger an EventFd
+                                   vhu_console.write().unwrap().output_buffer = character.to_string();
+                   vhu_console.write().unwrap().rx_event.write(1).unwrap();
+                }
+                       }
+       });
+    }
+}
+
+/// VhostUserBackendMut trait methods
+impl VhostUserBackendMut<VringRwLock, ()>
+    for VhostUserConsoleBackend
+{
+    fn num_queues(&self) -> usize {
+               log::trace!("num_queues: {:?}", NUM_QUEUES);
+        NUM_QUEUES
+    }
+
+    fn max_queue_size(&self) -> usize {
+               log::trace!("max_queue_size: {:?}", QUEUE_SIZE);
+        QUEUE_SIZE
+    }
+
+    fn features(&self) -> u64 {
+        // this matches the current libvhost defaults except VHOST_F_LOG_ALL
+        let features = 1 << VIRTIO_F_VERSION_1
+            | 1 << VIRTIO_F_NOTIFY_ON_EMPTY
+            | 1 << VIRTIO_RING_F_EVENT_IDX
+                       | 1 << VIRTIO_CONSOLE_F_EMERG_WRITE
+            | 1 << VIRTIO_RING_F_INDIRECT_DESC
+                       | 1 << VIRTIO_CONSOLE_F_MULTIPORT // This could be disabled
+            | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
+
+               log::trace!("vhu_can->features: {:x}", features);
+               features
+    }
+
+       fn acked_features(&mut self, _features: u64) {
+               log::trace!("\nacked_features: 0x{:x}\n", _features);
+               self.acked_features = _features;
+       }
+
+    fn protocol_features(&self) -> VhostUserProtocolFeatures {
+        let protocol_features = VhostUserProtocolFeatures::MQ
+            | VhostUserProtocolFeatures::CONFIG
+            | VhostUserProtocolFeatures::REPLY_ACK;
+
+               log::trace!("protocol_features: {:x}", protocol_features);
+               protocol_features
+    }
+
+    fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
+        // SAFETY: The layout of the structure is fixed and can be initialized by
+        // reading its content from byte array.
+               log::trace!("vhu_can->get_config");
+        unsafe {
+            from_raw_parts(
+                self.controller.write().unwrap()
+                    .config()
+                    .as_slice()
+                    .as_ptr()
+                    .offset(offset as isize) as *const _ as *const _,
+                size as usize,
+            )
+            .to_vec()
+        }
+    }
+
+    fn set_event_idx(&mut self, enabled: bool) {
+        dbg!(self.event_idx = enabled);
+    }
+
+    fn update_memory(&mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>) -> IoResult<()> {
+               log::trace!("update_memory\n");
+        self.mem = Some(mem);
+        Ok(())
+    }
+
+    fn handle_event(
+        &mut self,
+        device_event: u16,
+        evset: EventSet,
+        vrings: &[VringRwLock],
+        _thread_id: usize,
+    ) -> IoResult<bool> {
+               log::trace!("\nhandle_event:");
+
+               if device_event == RX_QUEUE {
+                   log::trace!("RX_QUEUE\n");
+                   return Ok(false);
+               };
+
+               if device_event == CTRL_RX_QUEUE {
+                   log::trace!("CTRL_RX_QUEUE\n");
+                       if !self.ready {
+                               return Ok(false);
+                       }
+               };
+
+               let vring = if device_event == BACKEND_EFD {
+                   log::trace!("BACKEND_EFD\n");
+                   &vrings[RX_QUEUE as usize]
+               } else if device_event == BACKEND_RX_EFD {
+                   log::trace!("BACKEND_RX_EFD\n");
+                   &vrings[CTRL_RX_QUEUE as usize]
+               } else {
+                       &vrings[device_event as usize]
+               };
+
+        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 {
+                    TX_QUEUE => {
+                                       self.ready_to_write = true;
+                        self.process_tx_queue(vring)
+                    },
+                    CTRL_RX_QUEUE => self.process_ctrl_rx_queue(vring),
+                                       CTRL_TX_QUEUE => {
+                                               let rx_ctrl_vring = &vrings[CTRL_RX_QUEUE as usize];
+                                               self.process_ctrl_tx_queue(vring, rx_ctrl_vring)
+                                       },
+                    BACKEND_EFD => {
+                                               let _ = self.rx_event.read();
+                                               self.process_rx_queue(vring)
+                                       },
+                    BACKEND_RX_EFD => {
+                                               let _ = self.rx_ctrl_event.read();
+                                               self.process_ctrl_rx_queue(vring)
+                                       },
+                    _ => Err(Error::HandleEventUnknown.into()),
+                }?;
+                if !vring.enable_notification().unwrap() {
+                    break;
+                }
+            }
+        } else {                                                                           
+            // Without EVENT_IDX, a single call is enough.                                 
+            match device_event {
+                TX_QUEUE => {
+                               self.ready_to_write = true;
+                    self.process_tx_queue(vring)
+                },
+                CTRL_RX_QUEUE => self.process_ctrl_rx_queue(vring),
+                               CTRL_TX_QUEUE => {
+                                       let rx_ctrl_vring = &vrings[CTRL_RX_QUEUE as usize];
+                                       self.process_ctrl_tx_queue(vring, rx_ctrl_vring)
+                               },
+                BACKEND_EFD => {
+                                       let _ = self.rx_event.read();
+                                       self.process_rx_queue(vring)
+                               },
+                BACKEND_RX_EFD => {
+                                       let _ = self.rx_ctrl_event.read();
+                                       self.process_ctrl_rx_queue(vring)
+                               },
+                _ => Err(Error::HandleEventUnknown.into()),
+            }?;
+        }
+        Ok(false)
+    }
+
+    fn exit_event(&self, _thread_index: usize) -> Option<EventFd> {
+               dbg!("exit_event\n");
+        self.exit_event.try_clone().ok()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_virtio_console_control_byte_valued() {
+        let control = VirtioConsoleControl {
+            id: Le32::from(1),
+            event: Le16::from(2),
+            value: Le16::from(3),
+        };
+
+        let bytes = control.to_le_bytes();
+
+        assert_eq!(bytes.len(), 10);
+    }
+
+    #[test]
+    fn test_vhost_user_console_backend_creation() {
+        let console_controller = Arc::new(RwLock::new(ConsoleController::new(String::from("test_console")).unwrap()));
+        let vhost_user_console_backend = VhostUserConsoleBackend::new(console_controller).unwrap();
+
+        assert_eq!(vhost_user_console_backend.acked_features, 0);
+        assert_eq!(vhost_user_console_backend.event_idx, false);
+        assert_eq!(vhost_user_console_backend.ready, false);
+        assert_eq!(vhost_user_console_backend.ready_to_write, false);
+        assert_eq!(vhost_user_console_backend.output_buffer, String::new());
+    }
+}
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc
new file mode 100644 (file)
index 0000000..6026714
--- /dev/null
@@ -0,0 +1,87 @@
+SRC_URI += " \
+    crate://crates.io/encode_unicode/0.3.6 \
+    crate://crates.io/unicode-width/0.1.11 \
+    crate://crates.io/console/0.14.1 \
+    crate://crates.io/lazy_static/1.4.0 \
+    crate://crates.io/serde_derive/1.0.193 \
+    crate://crates.io/autocfg/1.1.0 \
+    crate://crates.io/syn/1.0.109 \
+    crate://crates.io/serde/1.0.193 \
+    crate://crates.io/memoffset/0.7.0 \
+    crate://crates.io/static_assertions/1.1.0 \
+    crate://crates.io/pin-utils/0.1.0 \
+    crate://crates.io/cfg-if/1.0.0 \
+    crate://crates.io/neli-proc-macros/0.1.3 \
+    crate://crates.io/byteorder/1.5.0 \
+    crate://crates.io/either/1.9.0 \
+    crate://crates.io/nix/0.26.0 \
+    crate://crates.io/neli/0.6.4 \
+    crate://crates.io/nb/1.1.0 \
+    crate://crates.io/itertools/0.10.0 \
+    crate://crates.io/hex/0.4.3 \
+    crate://crates.io/embedded-can/0.4.1 \
+    crate://crates.io/byte_conv/0.1.1 \
+    crate://crates.io/queues/1.1.0 \
+    crate://crates.io/socketcan/3.3.0 \
+    crate://crates.io/aho-corasick/1.0.2 \
+    crate://crates.io/anstream/0.3.2 \
+    crate://crates.io/anstyle/1.0.1 \
+    crate://crates.io/anstyle-parse/0.2.1 \
+    crate://crates.io/anstyle-query/1.0.0 \
+    crate://crates.io/anstyle-wincon/1.0.1 \
+    crate://crates.io/arc-swap/1.6.0 \
+    crate://crates.io/assert_matches/1.5.0 \
+    crate://crates.io/bitflags/1.3.2 \
+    crate://crates.io/bitflags/2.3.3 \
+    crate://crates.io/cc/1.0.79 \
+    crate://crates.io/clap/4.2.5 \
+    crate://crates.io/clap_builder/4.2.5 \
+    crate://crates.io/clap_derive/4.2.0 \
+    crate://crates.io/clap_lex/0.4.0 \
+    crate://crates.io/colorchoice/1.0.0 \
+    crate://crates.io/env_logger/0.10.0 \
+    crate://crates.io/errno/0.3.1 \
+    crate://crates.io/errno-dragonfly/0.1.2 \
+    crate://crates.io/heck/0.4.1 \
+    crate://crates.io/hermit-abi/0.3.2 \
+    crate://crates.io/humantime/2.1.0 \
+    crate://crates.io/is-terminal/0.4.9 \
+    crate://crates.io/libc/0.2.147 \
+    crate://crates.io/linux-raw-sys/0.4.3 \
+    crate://crates.io/log/0.4.19 \
+    crate://crates.io/memchr/2.5.0 \
+    crate://crates.io/once_cell/1.18.0 \
+    crate://crates.io/proc-macro2/1.0.67 \
+    crate://crates.io/quote/1.0.29 \
+    crate://crates.io/regex/1.9.1 \
+    crate://crates.io/regex-automata/0.3.2 \
+    crate://crates.io/regex-syntax/0.7.4 \
+    crate://crates.io/rustix/0.38.3 \
+    crate://crates.io/strsim/0.10.0 \
+    crate://crates.io/syn/2.0.28 \
+    crate://crates.io/termcolor/1.2.0 \
+    crate://crates.io/thiserror/1.0.41 \
+    crate://crates.io/thiserror-impl/1.0.41 \
+    crate://crates.io/unicode-ident/1.0.11 \
+    crate://crates.io/utf8parse/0.2.1 \
+    crate://crates.io/vhost/0.8.0 \
+    crate://crates.io/vhost-user-backend/0.10.0 \
+    crate://crates.io/virtio-bindings/0.2.1 \
+    crate://crates.io/virtio-queue/0.9.0 \
+    crate://crates.io/vm-memory/0.12.0 \
+    crate://crates.io/vmm-sys-util/0.11.1 \
+    crate://crates.io/winapi/0.3.9 \
+    crate://crates.io/winapi-i686-pc-windows-gnu/0.4.0 \
+    crate://crates.io/winapi-util/0.1.5 \
+    crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \
+    crate://crates.io/windows-sys/0.45.0 \
+    crate://crates.io/windows-targets/0.42.1 \
+    crate://crates.io/windows_aarch64_gnullvm/0.42.1 \
+    crate://crates.io/windows_aarch64_msvc/0.42.1 \
+    crate://crates.io/windows_i686_gnu/0.42.1 \
+    crate://crates.io/windows_i686_msvc/0.42.1 \
+    crate://crates.io/windows_x86_64_gnu/0.42.1 \
+    crate://crates.io/windows_x86_64_gnullvm/0.42.1 \
+    crate://crates.io/windows_x86_64_msvc/0.42.1 \
+"
+
diff --git a/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb
new file mode 100644 (file)
index 0000000..e02133b
--- /dev/null
@@ -0,0 +1,21 @@
+SUMMARY = "vhost Console backend device"
+DESCRIPTION = "A vhost-user backend that emulates a VirtIO console device"
+HOMEPAGE = "https://gerrit.automotivelinux.org"
+
+FILESEXTRAPATHS:prepend := "${THISDIR}:" 
+EXTRAPATHS:prepend := "${THISDIR}:" 
+
+SRC_URI = " file://. "
+
+LICENSE = "Apache-2.0"
+LICENSE = "BSD-3-Clause"
+
+LIC_FILES_CHKSUM = "\
+    file://LICENSE-APACHE;md5=3b83ef96387f14655fc854ddc3c6bd57 \
+    file://LICENSE-BSD-3-Clause;md5=2489db1359f496fff34bd393df63947e \
+"
+
+inherit cargo
+inherit pkgconfig
+
+include vhost-device-console-crates.inc