diff options
author | Michele Paolino <m.paolino@virtualopensystems.com> | 2023-12-18 13:48:17 +0000 |
---|---|---|
committer | Jan-Simon Moeller <jsmoeller@linuxfoundation.org> | 2023-12-29 15:58:49 +0000 |
commit | 5ea74929e0cc276eda3f69737097c0903d771e95 (patch) | |
tree | 74d9c2881301e0c59561a6a8fc2bda3ed8a83123 | |
parent | fd0411d38d889b74958bcb6e461c80f65133d737 (diff) |
Vhost-user-console device and can license fixes
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, 1755 insertions, 2 deletions
diff --git a/meta-egvirt/conf/include/agl-egvirt.inc b/meta-egvirt/conf/include/agl-egvirt.inc index 40cb4ca7..bcc3e1d4 100644 --- a/meta-egvirt/conf/include/agl-egvirt.inc +++ b/meta-egvirt/conf/include/agl-egvirt.inc @@ -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 index e69de29b..00000000 --- a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can-0.1.0/LICENSE +++ /dev/null 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 index 00000000..d6456956 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/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/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 index 00000000..dd975d98 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-can/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/vhost-device-can_0.1.0.bb b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb index c18e7517..4db8ebea 100644 --- a/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb +++ b/meta-egvirt/recipes-extended/vhost-device-can/vhost-device-can_0.1.0.bb @@ -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 index 00000000..51d3f040 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + diff --git a/meta-egvirt/recipes-extended/vhost-device-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 index 00000000..c2f9f71e --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/Cargo.toml @@ -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 index 00000000..d6456956 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-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-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 index 00000000..dd975d98 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-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-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 index 00000000..b2c92440 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/README.md @@ -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 index 00000000..15b50a38 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/backend.rs @@ -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 index 00000000..2e2972b2 --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/console.rs @@ -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 index 00000000..cceafb4f --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/main.rs @@ -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 index 00000000..ebddb00d --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-0.1.0/src/vhu_console.rs @@ -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 index 00000000..6026714e --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console-crates.inc @@ -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 index 00000000..e02133bd --- /dev/null +++ b/meta-egvirt/recipes-extended/vhost-device-console/vhost-device-console_0.1.0.bb @@ -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 |