├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rustfmt.toml └── src ├── bitflags.rs ├── driver_notifications.rs ├── features.rs ├── fs.rs ├── lib.rs ├── mmio.rs ├── net.rs ├── pci.rs ├── pvirtq.rs ├── virtq ├── alloc.rs └── mod.rs ├── volatile.rs └── vsock.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | merge_group: 7 | 8 | env: 9 | RUSTFLAGS: -Dwarnings 10 | RUSTDOCFLAGS: -Dwarnings 11 | 12 | jobs: 13 | check: 14 | name: Check 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: dtolnay/rust-toolchain@stable 19 | - uses: taiki-e/install-action@cargo-hack 20 | - run: cargo hack check --feature-powerset --exclude-features nightly --no-dev-deps 21 | 22 | clippy: 23 | name: Clippy 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: dtolnay/rust-toolchain@stable 28 | - run: cargo clippy --features alloc,mmio,pci,zerocopy 29 | 30 | doc: 31 | name: Doc 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v4 35 | - uses: dtolnay/rust-toolchain@stable 36 | - run: cargo doc --features alloc,mmio,pci,zerocopy 37 | 38 | fmt: 39 | name: Format 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: dtolnay/rust-toolchain@nightly 44 | with: 45 | components: rustfmt 46 | - run: cargo fmt --all --check 47 | 48 | test: 49 | name: Test 50 | runs-on: ubuntu-latest 51 | steps: 52 | - uses: actions/checkout@v4 53 | - uses: dtolnay/rust-toolchain@stable 54 | - run: cargo test --features alloc,mmio,pci,zerocopy 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "allocator-api2" 7 | version = "0.2.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 10 | 11 | [[package]] 12 | name = "bit_field" 13 | version = "0.10.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 16 | 17 | [[package]] 18 | name = "bitfield-struct" 19 | version = "0.9.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "45ced5210f4878484de1a151cbef1cbff7afe0c1a005e4448e8a4ec76275c7a0" 22 | dependencies = [ 23 | "proc-macro2", 24 | "quote", 25 | "syn", 26 | ] 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "2.6.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 33 | 34 | [[package]] 35 | name = "endian-num" 36 | version = "0.2.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "2b4a54dfedecb1dc004776a597db0c858cf6c31579296734511309291b8399ce" 39 | dependencies = [ 40 | "bitflags", 41 | "zerocopy", 42 | "zerocopy-derive", 43 | ] 44 | 45 | [[package]] 46 | name = "num_enum" 47 | version = "0.7.3" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 50 | dependencies = [ 51 | "num_enum_derive", 52 | ] 53 | 54 | [[package]] 55 | name = "num_enum_derive" 56 | version = "0.7.3" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 59 | dependencies = [ 60 | "proc-macro2", 61 | "quote", 62 | "syn", 63 | ] 64 | 65 | [[package]] 66 | name = "pci_types" 67 | version = "0.10.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "c4325c6aa3cca3373503b1527e75756f9fbfe5fd76be4b4c8a143ee47430b8e0" 70 | dependencies = [ 71 | "bit_field", 72 | "bitflags", 73 | ] 74 | 75 | [[package]] 76 | name = "proc-macro2" 77 | version = "1.0.87" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" 80 | dependencies = [ 81 | "unicode-ident", 82 | ] 83 | 84 | [[package]] 85 | name = "quote" 86 | version = "1.0.37" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 89 | dependencies = [ 90 | "proc-macro2", 91 | ] 92 | 93 | [[package]] 94 | name = "syn" 95 | version = "2.0.79" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 98 | dependencies = [ 99 | "proc-macro2", 100 | "quote", 101 | "unicode-ident", 102 | ] 103 | 104 | [[package]] 105 | name = "unicode-ident" 106 | version = "1.0.13" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 109 | 110 | [[package]] 111 | name = "virtio-spec" 112 | version = "0.2.0" 113 | dependencies = [ 114 | "allocator-api2", 115 | "bitfield-struct", 116 | "bitflags", 117 | "endian-num", 118 | "num_enum", 119 | "pci_types", 120 | "volatile", 121 | "volatile-macro", 122 | "zerocopy", 123 | "zerocopy-derive", 124 | ] 125 | 126 | [[package]] 127 | name = "volatile" 128 | version = "0.6.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "af8ca9a5d4debca0633e697c88269395493cebf2e10db21ca2dbde37c1356452" 131 | 132 | [[package]] 133 | name = "volatile-macro" 134 | version = "0.6.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "65c67ce935f3b4329e473ecaff7bab444fcdc3d1d19f8bae61fabfa90b84f93e" 137 | dependencies = [ 138 | "proc-macro2", 139 | "quote", 140 | "syn", 141 | ] 142 | 143 | [[package]] 144 | name = "zerocopy" 145 | version = "0.8.5" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "6129d25825e874589a0e529175dd060c13dab4f3d960c6a0b711e5535b598bb2" 148 | dependencies = [ 149 | "zerocopy-derive", 150 | ] 151 | 152 | [[package]] 153 | name = "zerocopy-derive" 154 | version = "0.8.5" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "d917df3784b4e2f5deb708d14623b2c02833890e1aa7a5dd1088998e8e9402b1" 157 | dependencies = [ 158 | "proc-macro2", 159 | "quote", 160 | "syn", 161 | ] 162 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio-spec" 3 | version = "0.2.0" 4 | authors = ["Martin Kröning "] 5 | edition = "2021" 6 | description = "Definitions from the Virtual I/O Device (VIRTIO) specification." 7 | repository = "https://github.com/rust-osdev/virtio-spec-rs" 8 | license = "MIT OR Apache-2.0" 9 | keywords = ["virtio", "specification", "driver", "net", "pci"] 10 | categories = ["external-ffi-bindings", "no-std::no-alloc"] 11 | 12 | [package.metadata.docs.rs] 13 | all-features = true 14 | rustdoc-args = ["--cfg", "docsrs"] 15 | 16 | [dependencies] 17 | allocator-api2 = { version = "0.2", default-features = false, features = ["alloc"], optional = true } 18 | bitfield-struct = "0.9" 19 | bitflags = "2" 20 | endian-num = { version = "0.2", features = ["bitflags", "linux-types"] } 21 | num_enum = { version = "0.7", default-features = false } 22 | pci_types = { version = "0.10", optional = true } 23 | volatile = "0.6" 24 | volatile-macro = "0.6" 25 | zerocopy = { version = "0.8", optional = true, default-features = false } 26 | zerocopy-derive = { version = "0.8", optional = true } 27 | 28 | [features] 29 | alloc = ["dep:allocator-api2"] 30 | mmio = [] 31 | nightly = ["allocator-api2/nightly"] 32 | pci = ["dep:pci_types"] 33 | zerocopy = ["dep:zerocopy", "dep:zerocopy-derive", "endian-num/zerocopy"] 34 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # virtio-spec-rs 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/virtio-spec)](https://crates.io/crates/virtio-spec) 4 | [![docs.rs](https://img.shields.io/docsrs/virtio-spec)](https://docs.rs/virtio-spec) 5 | [![CI](https://github.com/rust-osdev/virtio-spec-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-osdev/virtio-spec-rs/actions/workflows/ci.yml) 6 | 7 | This crate contains the Rust equivalents of the definitions from the [Virtual I/O Device (VIRTIO) Specification](https://github.com/oasis-tcs/virtio-spec). 8 | This crate aims to be unopinionated regarding actual VIRTIO drivers that are implemented on top of this crate. 9 | 10 | For API documentation, see the [docs]. 11 | 12 | [docs]: https://docs.rs/virtio-spec 13 | 14 | ## License 15 | 16 | Licensed under either of 17 | 18 | * Apache License, Version 2.0 19 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 20 | * MIT license 21 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 22 | 23 | at your option. 24 | 25 | ### Contribution 26 | 27 | Unless you explicitly state otherwise, any contribution intentionally submitted 28 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 29 | dual licensed as above, without any additional terms or conditions. 30 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | format_code_in_doc_comments = true 2 | group_imports = "StdExternalCrate" 3 | imports_granularity = "Module" 4 | -------------------------------------------------------------------------------- /src/bitflags.rs: -------------------------------------------------------------------------------- 1 | macro_rules! _bitflags_base { 2 | ( 3 | $(#[$outer:meta])* 4 | $vis:vis struct $BitFlags:ident: $T:ty; 5 | 6 | $($t:tt)* 7 | ) => { 8 | #[cfg_attr( 9 | feature = "zerocopy", 10 | derive( 11 | zerocopy_derive::KnownLayout, 12 | zerocopy_derive::Immutable, 13 | zerocopy_derive::FromBytes, 14 | zerocopy_derive::IntoBytes, 15 | ) 16 | )] 17 | #[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] 18 | #[repr(transparent)] 19 | $(#[$outer])* 20 | $vis struct $BitFlags($T); 21 | 22 | impl ::core::fmt::Debug for $BitFlags { 23 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 24 | struct Inner<'a>(&'a $BitFlags); 25 | 26 | impl<'a> ::core::fmt::Debug for Inner<'a> { 27 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 28 | if self.0.is_empty() { 29 | f.write_str("0x0") 30 | } else { 31 | ::bitflags::parser::to_writer(self.0, f) 32 | } 33 | } 34 | } 35 | 36 | f.debug_tuple(::core::stringify!($BitFlags)) 37 | .field(&Inner(self)) 38 | .finish() 39 | } 40 | } 41 | 42 | _bitflags_base! { 43 | $($t)* 44 | } 45 | }; 46 | () => {}; 47 | } 48 | 49 | macro_rules! virtio_bitflags { 50 | ( 51 | $(#[$outer:meta])* 52 | $vis:vis struct $BitFlags:ident: $T:ty { 53 | $( 54 | $(#[$inner:ident $($args:tt)*])* 55 | const $Flag:tt = $value:expr; 56 | )* 57 | } 58 | 59 | $($t:tt)* 60 | ) => { 61 | _bitflags_base! { 62 | $(#[$outer])* 63 | $vis struct $BitFlags: $T; 64 | } 65 | 66 | ::bitflags::bitflags! { 67 | impl $BitFlags: $T { 68 | $( 69 | $(#[$inner $($args)*])* 70 | const $Flag = $value; 71 | )* 72 | 73 | const _ = !0; 74 | } 75 | } 76 | 77 | virtio_bitflags! { 78 | $($t)* 79 | } 80 | }; 81 | () => {}; 82 | } 83 | 84 | macro_rules! impl_fmt { 85 | ($Trait:ident for $SelfT:ty) => { 86 | impl ::core::fmt::$Trait for $SelfT { 87 | fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 88 | self.0.fmt(f) 89 | } 90 | } 91 | }; 92 | } 93 | 94 | macro_rules! endian_bitflags { 95 | ( 96 | $(#[$outer:meta])* 97 | $vis:vis struct $BitFlags:ident: $T:ty { 98 | $( 99 | $(#[$inner:ident $($args:tt)*])* 100 | const $Flag:tt = $value:expr; 101 | )* 102 | } 103 | 104 | $($t:tt)* 105 | ) => { 106 | _bitflags_base! { 107 | $(#[$outer])* 108 | $vis struct $BitFlags: $T; 109 | } 110 | 111 | impl $BitFlags { 112 | $( 113 | $(#[$inner $($args)*])* 114 | pub const $Flag: Self = Self(<$T>::from_ne($value)); 115 | )* 116 | } 117 | 118 | impl ::bitflags::Flags for $BitFlags { 119 | const FLAGS: &'static [::bitflags::Flag] = &[ 120 | $( 121 | ::bitflags::Flag::new(::core::stringify!($Flag), Self::$Flag), 122 | )* 123 | ::bitflags::Flag::new("", Self::all()), 124 | ]; 125 | 126 | type Bits = $T; 127 | 128 | fn from_bits_retain(bits: Self::Bits) -> Self { 129 | Self(bits) 130 | } 131 | 132 | fn bits(&self) -> Self::Bits { 133 | self.0 134 | } 135 | } 136 | 137 | impl $BitFlags{ 138 | /// Get a flags value with all bits unset. 139 | #[inline] 140 | pub const fn empty() -> Self { 141 | Self(<$T as ::bitflags::Bits>::EMPTY) 142 | } 143 | 144 | /// Get a flags value with all known bits set. 145 | #[inline] 146 | pub const fn all() -> Self { 147 | Self(<$T as ::bitflags::Bits>::ALL) 148 | } 149 | 150 | /// Get the underlying bits value. 151 | /// 152 | /// The returned value is exactly the bits set in this flags value. 153 | #[inline] 154 | pub const fn bits(&self) -> $T { 155 | self.0 156 | } 157 | 158 | /// Convert from a bits value. 159 | /// 160 | /// This method will return `None` if any unknown bits are set. 161 | #[inline] 162 | pub const fn from_bits(bits: $T) -> Option { 163 | Some(Self(bits)) 164 | } 165 | 166 | /// Convert from a bits value, unsetting any unknown bits. 167 | #[inline] 168 | pub const fn from_bits_truncate(bits: $T) -> Self { 169 | Self(bits) 170 | } 171 | 172 | /// Convert from a bits value exactly. 173 | #[inline] 174 | pub const fn from_bits_retain(bits: $T) -> Self { 175 | Self(bits) 176 | } 177 | 178 | /// Get a flags value with the bits of a flag with the given name set. 179 | /// 180 | /// This method will return `None` if `name` is empty or doesn't 181 | /// correspond to any named flag. 182 | #[inline] 183 | pub fn from_name(name: &str) -> Option { 184 | ::from_name(name) 185 | } 186 | 187 | /// Whether all bits in this flags value are unset. 188 | #[inline] 189 | pub const fn is_empty(&self) -> bool { 190 | self.bits().to_ne() == <$T as ::bitflags::Bits>::EMPTY.to_ne() 191 | } 192 | 193 | /// Whether all known bits in this flags value are set. 194 | #[inline] 195 | pub const fn is_all(&self) -> bool { 196 | Self::all().bits().to_ne() | self.bits().to_ne() == self.bits().to_ne() 197 | } 198 | 199 | /// Whether any set bits in a source flags value are also set in a target flags value. 200 | #[inline] 201 | pub const fn intersects(&self, other: Self) -> bool { 202 | self.bits().to_ne() & other.bits().to_ne() != <$T as ::bitflags::Bits>::EMPTY.to_ne() 203 | } 204 | 205 | /// Whether all set bits in a source flags value are also set in a target flags value. 206 | #[inline] 207 | pub const fn contains(&self, other: Self) -> bool { 208 | self.bits().to_ne() & other.bits().to_ne() == other.bits().to_ne() 209 | } 210 | 211 | /// The bitwise or (`|`) of the bits in two flags values. 212 | #[inline] 213 | pub fn insert(&mut self, other: Self) { 214 | *self = Self::from_bits_retain(self.bits()).union(other); 215 | } 216 | 217 | /// The intersection of a source flags value with the complement of a target flags value (`&!`). 218 | /// 219 | /// This method is not equivalent to `self & !other` when `other` has unknown bits set. 220 | /// `remove` won't truncate `other`, but the `!` operator will. 221 | #[inline] 222 | pub fn remove(&mut self, other: Self) { 223 | *self = Self::from_bits_retain(self.bits()).difference(other); 224 | } 225 | 226 | /// The bitwise exclusive-or (`^`) of the bits in two flags values. 227 | #[inline] 228 | pub fn toggle(&mut self, other: Self) { 229 | *self = Self::from_bits_retain(self.bits()).symmetric_difference(other); 230 | } 231 | 232 | /// Call `insert` when `value` is `true` or `remove` when `value` is `false`. 233 | #[inline] 234 | pub fn set(&mut self, other: Self, value: bool) { 235 | if value { 236 | self.insert(other); 237 | } else { 238 | self.remove(other); 239 | } 240 | } 241 | 242 | /// The bitwise and (`&`) of the bits in two flags values. 243 | #[inline] 244 | #[must_use] 245 | pub const fn intersection(self, other: Self) -> Self { 246 | Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() & other.bits().to_ne())) 247 | } 248 | 249 | /// The bitwise or (`|`) of the bits in two flags values. 250 | #[inline] 251 | #[must_use] 252 | pub const fn union(self, other: Self) -> Self { 253 | Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() | other.bits().to_ne())) 254 | } 255 | 256 | /// The intersection of a source flags value with the complement of a target flags value (`&!`). 257 | /// 258 | /// This method is not equivalent to `self & !other` when `other` has unknown bits set. 259 | /// `difference` won't truncate `other`, but the `!` operator will. 260 | #[inline] 261 | #[must_use] 262 | pub const fn difference(self, other: Self) -> Self { 263 | Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() & !other.bits().to_ne())) 264 | } 265 | 266 | /// The bitwise exclusive-or (`^`) of the bits in two flags values. 267 | #[inline] 268 | #[must_use] 269 | pub const fn symmetric_difference(self, other: Self) -> Self { 270 | Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() ^ other.bits().to_ne())) 271 | } 272 | 273 | /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. 274 | #[inline] 275 | #[must_use] 276 | pub const fn complement(self) -> Self { 277 | Self::from_bits_truncate(<$T>::from_ne(!self.bits().to_ne())) 278 | } 279 | } 280 | 281 | impl_fmt!(Binary for $BitFlags); 282 | impl_fmt!(Octal for $BitFlags); 283 | impl_fmt!(LowerHex for $BitFlags); 284 | impl_fmt!(UpperHex for $BitFlags); 285 | 286 | impl ::core::ops::BitOr for $BitFlags { 287 | type Output = Self; 288 | 289 | /// The bitwise or (`|`) of the bits in two flags values. 290 | #[inline] 291 | fn bitor(self, other: Self) -> Self { 292 | self.union(other) 293 | } 294 | } 295 | 296 | impl ::core::ops::BitOrAssign for $BitFlags { 297 | /// The bitwise or (`|`) of the bits in two flags values. 298 | #[inline] 299 | fn bitor_assign(&mut self, other: Self) { 300 | self.insert(other); 301 | } 302 | } 303 | 304 | impl ::core::ops::BitXor for $BitFlags { 305 | type Output = Self; 306 | 307 | /// The bitwise exclusive-or (`^`) of the bits in two flags values. 308 | #[inline] 309 | fn bitxor(self, other: Self) -> Self { 310 | self.symmetric_difference(other) 311 | } 312 | } 313 | 314 | impl ::core::ops::BitXorAssign for $BitFlags { 315 | /// The bitwise exclusive-or (`^`) of the bits in two flags values. 316 | #[inline] 317 | fn bitxor_assign(&mut self, other: Self) { 318 | self.toggle(other); 319 | } 320 | } 321 | 322 | impl ::core::ops::BitAnd for $BitFlags { 323 | type Output = Self; 324 | 325 | /// The bitwise and (`&`) of the bits in two flags values. 326 | #[inline] 327 | fn bitand(self, other: Self) -> Self { 328 | self.intersection(other) 329 | } 330 | } 331 | 332 | impl ::core::ops::BitAndAssign for $BitFlags { 333 | /// The bitwise and (`&`) of the bits in two flags values. 334 | #[inline] 335 | fn bitand_assign(&mut self, other: Self) { 336 | *self = Self::from_bits_retain(self.bits()).intersection(other); 337 | } 338 | } 339 | 340 | impl ::core::ops::Sub for $BitFlags { 341 | type Output = Self; 342 | 343 | /// The intersection of a source flags value with the complement of a target flags value (`&!`). 344 | /// 345 | /// This method is not equivalent to `self & !other` when `other` has unknown bits set. 346 | /// `difference` won't truncate `other`, but the `!` operator will. 347 | #[inline] 348 | fn sub(self, other: Self) -> Self { 349 | self.difference(other) 350 | } 351 | } 352 | 353 | impl ::core::ops::SubAssign for $BitFlags { 354 | /// The intersection of a source flags value with the complement of a target flags value (`&!`). 355 | /// 356 | /// This method is not equivalent to `self & !other` when `other` has unknown bits set. 357 | /// `difference` won't truncate `other`, but the `!` operator will. 358 | #[inline] 359 | fn sub_assign(&mut self, other: Self) { 360 | self.remove(other); 361 | } 362 | } 363 | 364 | impl ::core::ops::Not for $BitFlags { 365 | type Output = Self; 366 | 367 | /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. 368 | #[inline] 369 | fn not(self) -> Self { 370 | self.complement() 371 | } 372 | } 373 | 374 | impl ::core::iter::Extend<$BitFlags> for $BitFlags { 375 | /// The bitwise or (`|`) of the bits in each flags value. 376 | fn extend(&mut self, iterator: T) 377 | where 378 | T: ::core::iter::IntoIterator, 379 | { 380 | for item in iterator { 381 | self.insert(item) 382 | } 383 | } 384 | } 385 | 386 | impl ::core::iter::FromIterator<$BitFlags> for $BitFlags { 387 | /// The bitwise or (`|`) of the bits in each flags value. 388 | fn from_iter(iterator: T) -> Self 389 | where 390 | T: ::core::iter::IntoIterator, 391 | { 392 | use ::core::iter::Extend; 393 | 394 | let mut result = Self::empty(); 395 | result.extend(iterator); 396 | result 397 | } 398 | } 399 | 400 | impl $BitFlags { 401 | /// Yield a set of contained flags values. 402 | /// 403 | /// Each yielded flags value will correspond to a defined named flag. Any unknown bits 404 | /// will be yielded together as a final flags value. 405 | #[inline] 406 | pub fn iter(&self) -> ::bitflags::iter::Iter { 407 | ::bitflags::Flags::iter(self) 408 | } 409 | 410 | /// Yield a set of contained named flags values. 411 | /// 412 | /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags. 413 | /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded. 414 | #[inline] 415 | pub fn iter_names(&self) -> ::bitflags::iter::IterNames { 416 | ::bitflags::Flags::iter_names(self) 417 | } 418 | } 419 | 420 | impl ::core::iter::IntoIterator for $BitFlags { 421 | type Item = Self; 422 | type IntoIter = ::bitflags::iter::Iter; 423 | fn into_iter(self) -> Self::IntoIter { 424 | self.iter() 425 | } 426 | } 427 | 428 | endian_bitflags! { 429 | $($t)* 430 | } 431 | }; 432 | () => {}; 433 | } 434 | -------------------------------------------------------------------------------- /src/driver_notifications.rs: -------------------------------------------------------------------------------- 1 | use bitfield_struct::bitfield; 2 | 3 | use crate::le32; 4 | 5 | /// Notification Data. 6 | #[bitfield(u32, repr = le32, from = le32::from_ne, into = le32::to_ne)] 7 | pub struct NotificationData { 8 | /// VQ number to be notified. 9 | pub vqn: u16, 10 | 11 | /// Offset 12 | /// within the ring where the next available ring entry 13 | /// will be written. 14 | /// When [`VIRTIO_F_RING_PACKED`] has not been negotiated this refers to the 15 | /// 15 least significant bits of the available index. 16 | /// When `VIRTIO_F_RING_PACKED` has been negotiated this refers to the offset 17 | /// (in units of descriptor entries) 18 | /// within the descriptor ring where the next available 19 | /// descriptor will be written. 20 | /// 21 | /// [`VIRTIO_F_RING_PACKED`]: F::RING_PACKED 22 | #[bits(15)] 23 | pub next_off: u16, 24 | 25 | /// Wrap Counter. 26 | /// With [`VIRTIO_F_RING_PACKED`] this is the wrap counter 27 | /// referring to the next available descriptor. 28 | /// Without `VIRTIO_F_RING_PACKED` this is the most significant bit 29 | /// (bit 15) of the available index. 30 | /// 31 | /// [`VIRTIO_F_RING_PACKED`]: F::RING_PACKED 32 | #[bits(1)] 33 | pub next_wrap: u8, 34 | } 35 | 36 | impl NotificationData { 37 | const NEXT_IDX_BITS: usize = 16; 38 | const NEXT_IDX_OFFSET: usize = 16; 39 | 40 | /// Available index 41 | /// 42 | ///
43 | /// 44 | /// This collides with [`Self::next_off`] and [`Self::next_wrap`]. 45 | /// 46 | ///
47 | /// 48 | /// Bits: 16..32 49 | pub const fn next_idx(&self) -> u16 { 50 | let mask = u32::MAX >> (u32::BITS - Self::NEXT_IDX_BITS as u32); 51 | let this = (le32::to_ne(self.0) >> Self::NEXT_IDX_OFFSET) & mask; 52 | this as u16 53 | } 54 | 55 | /// Available index 56 | /// 57 | ///
58 | /// 59 | /// This collides with [`Self::with_next_off`] and [`Self::with_next_wrap`]. 60 | /// 61 | ///
62 | /// 63 | /// Bits: 16..32 64 | pub const fn with_next_idx(self, value: u16) -> Self { 65 | let mask = u32::MAX >> (u32::BITS - Self::NEXT_IDX_BITS as u32); 66 | let bits = le32::to_ne(self.0) & !(mask << Self::NEXT_IDX_OFFSET) 67 | | (value as u32 & mask) << Self::NEXT_IDX_OFFSET; 68 | Self(le32::from_ne(bits)) 69 | } 70 | 71 | /// Available index 72 | /// 73 | ///
74 | /// 75 | /// This collides with [`Self::set_next_off`] and [`Self::set_next_wrap`]. 76 | /// 77 | ///
78 | /// 79 | /// Bits: 16..32 80 | pub fn set_next_idx(&mut self, value: u16) { 81 | *self = self.with_next_idx(value); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/features.rs: -------------------------------------------------------------------------------- 1 | //! Feature Bits 2 | 3 | use crate::le128; 4 | 5 | /// Feature Bits 6 | #[doc(alias = "VIRTIO_F")] 7 | pub trait FeatureBits: bitflags::Flags 8 | where 9 | Self: From + AsRef + AsMut, 10 | F: From + AsRef + AsMut, 11 | { 12 | /// Returns the feature that this feature requires. 13 | /// 14 | /// If `self` is a single feature and multiple features are returned, `self` requires only one of them. 15 | /// 16 | /// # Examples 17 | /// 18 | /// ``` 19 | /// # use virtio_spec as virtio; 20 | /// use virtio::FeatureBits; 21 | /// 22 | /// assert_eq!( 23 | /// virtio::net::F::GUEST_TSO4.requirements(), 24 | /// virtio::net::F::GUEST_CSUM 25 | /// ); 26 | /// assert_eq!( 27 | /// virtio::net::F::GUEST_ECN.requirements(), 28 | /// virtio::net::F::GUEST_TSO4 | virtio::net::F::GUEST_TSO6 29 | /// ); 30 | /// ``` 31 | fn requirements(&self) -> Self { 32 | Self::empty() 33 | } 34 | 35 | /// Returns `true` if all internal feature requirements are satisfied. 36 | /// 37 | /// # Examples 38 | /// 39 | /// ``` 40 | /// # use virtio_spec as virtio; 41 | /// use virtio::FeatureBits; 42 | /// 43 | /// assert!((virtio::net::F::GUEST_TSO4 | virtio::net::F::GUEST_CSUM).requirements_satisfied()); 44 | /// assert!( 45 | /// (virtio::net::F::GUEST_ECN | virtio::net::F::GUEST_TSO4 | virtio::net::F::GUEST_CSUM) 46 | /// .requirements_satisfied() 47 | /// ); 48 | /// ``` 49 | fn requirements_satisfied(&self) -> bool { 50 | self.iter() 51 | .map(|feature| feature.requirements()) 52 | .filter(|requirements| !requirements.is_empty()) 53 | .all(|requirements| self.intersects(requirements)) 54 | } 55 | } 56 | 57 | endian_bitflags! { 58 | /// Device-independent Feature Bits 59 | #[doc(alias = "VIRTIO_F")] 60 | pub struct F: le128 { 61 | /// Negotiating this feature indicates 62 | /// that the driver can use descriptors with the VIRTQ_DESC_F_INDIRECT 63 | /// flag set, as described in _Basic Facilities of a Virtio 64 | /// Device / Virtqueues / The Virtqueue Descriptor Table / Indirect 65 | /// Descriptors_ _Basic Facilities of a Virtio Device / 66 | /// Virtqueues / The Virtqueue Descriptor Table / Indirect 67 | /// Descriptors_ and _Packed Virtqueues / Indirect Flag: Scatter-Gather Support_ _Packed Virtqueues / Indirect Flag: Scatter-Gather Support_. 68 | #[doc(alias = "VIRTIO_F_INDIRECT_DESC")] 69 | const INDIRECT_DESC = 1 << 28; 70 | 71 | /// This feature enables the _used_event_ 72 | /// and the _avail_event_ fields as described in 73 | /// _Basic Facilities of a Virtio Device / Virtqueues / Used Buffer Notification Suppression_, _Basic Facilities of a Virtio Device / Virtqueues / The Virtqueue Used Ring_ and _Packed Virtqueues / Driver and Device Event Suppression_. 74 | #[doc(alias = "VIRTIO_F_EVENT_IDX")] 75 | const EVENT_IDX = 1 << 29; 76 | 77 | /// This indicates compliance with this 78 | /// specification, giving a simple way to detect legacy devices or drivers. 79 | #[doc(alias = "VIRTIO_F_VERSION_1")] 80 | const VERSION_1 = 1 << 32; 81 | 82 | /// This feature indicates that 83 | /// the device can be used on a platform where device access to data 84 | /// in memory is limited and/or translated. E.g. this is the case if the device can be located 85 | /// behind an IOMMU that translates bus addresses from the device into physical 86 | /// addresses in memory, if the device can be limited to only access 87 | /// certain memory addresses or if special commands such as 88 | /// a cache flush can be needed to synchronise data in memory with 89 | /// the device. Whether accesses are actually limited or translated 90 | /// is described by platform-specific means. 91 | /// If this feature bit is set to 0, then the device 92 | /// has same access to memory addresses supplied to it as the 93 | /// driver has. 94 | /// In particular, the device will always use physical addresses 95 | /// matching addresses used by the driver (typically meaning 96 | /// physical addresses used by the CPU) 97 | /// and not translated further, and can access any address supplied to it by 98 | /// the driver. When clear, this overrides any platform-specific description of 99 | /// whether device access is limited or translated in any way, e.g. 100 | /// whether an IOMMU may be present. 101 | #[doc(alias = "VIRTIO_F_ACCESS_PLATFORM")] 102 | const ACCESS_PLATFORM = 1 << 33; 103 | 104 | /// This feature indicates 105 | /// support for the packed virtqueue layout as described in 106 | /// _Basic Facilities of a Virtio Device / Packed Virtqueues_ _Basic Facilities of a Virtio Device / Packed Virtqueues_. 107 | #[doc(alias = "VIRTIO_F_RING_PACKED")] 108 | const RING_PACKED = 1 << 34; 109 | 110 | /// This feature indicates 111 | /// that all buffers are used by the device in the same 112 | /// order in which they have been made available. 113 | #[doc(alias = "VIRTIO_F_IN_ORDER")] 114 | const IN_ORDER = 1 << 35; 115 | 116 | /// This feature indicates 117 | /// that memory accesses by the driver and the device are ordered 118 | /// in a way described by the platform. 119 | /// 120 | /// If this feature bit is negotiated, the ordering in effect for any 121 | /// memory accesses by the driver that need to be ordered in a specific way 122 | /// with respect to accesses by the device is the one suitable for devices 123 | /// described by the platform. This implies that the driver needs to use 124 | /// memory barriers suitable for devices described by the platform; e.g. 125 | /// for the PCI transport in the case of hardware PCI devices. 126 | /// 127 | /// If this feature bit is not negotiated, then the device 128 | /// and driver are assumed to be implemented in software, that is 129 | /// they can be assumed to run on identical CPUs 130 | /// in an SMP configuration. 131 | /// Thus a weaker form of memory barriers is sufficient 132 | /// to yield better performance. 133 | #[doc(alias = "VIRTIO_F_ORDER_PLATFORM")] 134 | const ORDER_PLATFORM = 1 << 36; 135 | 136 | /// This feature indicates that 137 | /// the device supports Single Root I/O Virtualization. 138 | /// Currently only PCI devices support this feature. 139 | #[doc(alias = "VIRTIO_F_SR_IOV")] 140 | const SR_IOV = 1 << 37; 141 | 142 | /// This feature indicates 143 | /// that the driver passes extra data (besides identifying the virtqueue) 144 | /// in its device notifications. 145 | /// See _Virtqueues / Driver notifications_ _Virtqueues / Driver notifications_. 146 | #[doc(alias = "VIRTIO_F_NOTIFICATION_DATA")] 147 | const NOTIFICATION_DATA = 1 << 38; 148 | 149 | /// This feature indicates that the driver 150 | /// uses the data provided by the device as a virtqueue identifier in available 151 | /// buffer notifications. 152 | /// As mentioned in section _Virtqueues / Driver notifications_, when the 153 | /// driver is required to send an available buffer notification to the device, it 154 | /// sends the virtqueue number to be notified. The method of delivering 155 | /// notifications is transport specific. 156 | /// With the PCI transport, the device can optionally provide a per-virtqueue value 157 | /// for the driver to use in driver notifications, instead of the virtqueue number. 158 | /// Some devices may benefit from this flexibility by providing, for example, 159 | /// an internal virtqueue identifier, or an internal offset related to the 160 | /// virtqueue number. 161 | /// 162 | /// This feature indicates the availability of such value. The definition of the 163 | /// data to be provided in driver notification and the delivery method is 164 | /// transport specific. 165 | /// For more details about driver notifications over PCI see _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_. 166 | #[doc(alias = "VIRTIO_F_NOTIF_CONFIG_DATA")] 167 | const NOTIF_CONFIG_DATA = 1 << 39; 168 | 169 | /// This feature indicates 170 | /// that the driver can reset a queue individually. 171 | /// See _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_. 172 | #[doc(alias = "VIRTIO_F_RING_RESET")] 173 | const RING_RESET = 1 << 40; 174 | } 175 | } 176 | 177 | impl AsRef for F { 178 | fn as_ref(&self) -> &F { 179 | self 180 | } 181 | } 182 | 183 | impl AsMut for F { 184 | fn as_mut(&mut self) -> &mut F { 185 | self 186 | } 187 | } 188 | 189 | impl FeatureBits for F {} 190 | 191 | macro_rules! feature_bits { 192 | ( 193 | $(#[$outer:meta])* 194 | $vis:vis struct $BitFlags:ident: $T:ty { 195 | $( 196 | $(#[$inner:ident $($args:tt)*])* 197 | const $Flag:tt = $value:expr; 198 | )* 199 | } 200 | 201 | $($t:tt)* 202 | ) => { 203 | endian_bitflags! { 204 | $(#[$outer])* 205 | $vis struct $BitFlags: $T { 206 | $( 207 | $(#[$inner $($args)*])* 208 | const $Flag = $value; 209 | )* 210 | 211 | /// Device-independent Bit. See [`virtio::F::INDIRECT_DESC`](crate::F::INDIRECT_DESC). 212 | const INDIRECT_DESC = $crate::F::INDIRECT_DESC.bits().to_ne(); 213 | 214 | /// Device-independent Bit. See [`virtio::F::EVENT_IDX`](crate::F::EVENT_IDX). 215 | const EVENT_IDX = $crate::F::EVENT_IDX.bits().to_ne(); 216 | 217 | /// Device-independent Bit. See [`virtio::F::VERSION_1`](crate::F::VERSION_1). 218 | const VERSION_1 = $crate::F::VERSION_1.bits().to_ne(); 219 | 220 | /// Device-independent Bit. See [`virtio::F::ACCESS_PLATFORM`](crate::F::ACCESS_PLATFORM). 221 | const ACCESS_PLATFORM = $crate::F::ACCESS_PLATFORM.bits().to_ne(); 222 | 223 | /// Device-independent Bit. See [`virtio::F::RING_PACKED`](crate::F::RING_PACKED). 224 | const RING_PACKED = $crate::F::RING_PACKED.bits().to_ne(); 225 | 226 | /// Device-independent Bit. See [`virtio::F::IN_ORDER`](crate::F::IN_ORDER). 227 | const IN_ORDER = $crate::F::IN_ORDER.bits().to_ne(); 228 | 229 | /// Device-independent Bit. See [`virtio::F::ORDER_PLATFORM`](crate::F::ORDER_PLATFORM). 230 | const ORDER_PLATFORM = $crate::F::ORDER_PLATFORM.bits().to_ne(); 231 | 232 | /// Device-independent Bit. See [`virtio::F::SR_IOV`](crate::F::SR_IOV). 233 | const SR_IOV = $crate::F::SR_IOV.bits().to_ne(); 234 | 235 | /// Device-independent Bit. See [`virtio::F::NOTIFICATION_DATA`](crate::F::NOTIFICATION_DATA). 236 | const NOTIFICATION_DATA = $crate::F::NOTIFICATION_DATA.bits().to_ne(); 237 | 238 | /// Device-independent Bit. See [`virtio::F::NOTIF_CONFIG_DATA`](crate::F::NOTIF_CONFIG_DATA). 239 | const NOTIF_CONFIG_DATA = $crate::F::NOTIF_CONFIG_DATA.bits().to_ne(); 240 | 241 | /// Device-independent Bit. See [`virtio::F::RING_RESET`](crate::F::RING_RESET). 242 | const RING_RESET = $crate::F::RING_RESET.bits().to_ne(); 243 | } 244 | } 245 | 246 | impl From<$crate::F> for $BitFlags { 247 | fn from(value: $crate::F) -> Self { 248 | Self::from_bits_retain(value.bits()) 249 | } 250 | } 251 | 252 | impl AsRef<$BitFlags> for $crate::F { 253 | fn as_ref(&self) -> &$BitFlags { 254 | unsafe { &*(self as *const Self as *const $BitFlags) } 255 | } 256 | } 257 | 258 | impl AsMut<$BitFlags> for $crate::F { 259 | fn as_mut(&mut self) -> &mut $BitFlags { 260 | unsafe { &mut *(self as *mut Self as *mut $BitFlags) } 261 | } 262 | } 263 | 264 | impl From<$BitFlags> for $crate::F { 265 | /// Returns the device-independent feature bits while retaining device-specific feature bits. 266 | fn from(value: $BitFlags) -> Self { 267 | $crate::F::from_bits_retain(value.bits()) 268 | } 269 | } 270 | 271 | impl AsRef<$crate::F> for $BitFlags { 272 | /// Returns a shared reference to the device-independent features while retaining device-specific feature bits. 273 | fn as_ref(&self) -> &$crate::F { 274 | unsafe { &*(self as *const Self as *const $crate::F) } 275 | } 276 | } 277 | 278 | impl AsMut<$crate::F> for $BitFlags { 279 | /// Returns a mutable reference to the device-independent features while retaining device-specific feature bits. 280 | fn as_mut(&mut self) -> &mut $crate::F { 281 | unsafe { &mut *(self as *mut Self as *mut $crate::F) } 282 | } 283 | } 284 | 285 | feature_bits! { 286 | $($t)* 287 | } 288 | }; 289 | () => {}; 290 | } 291 | 292 | pub mod net { 293 | use crate::le128; 294 | 295 | feature_bits! { 296 | /// Network Device Feature Bits 297 | #[doc(alias = "VIRTIO_NET_F")] 298 | pub struct F: le128 { 299 | /// Device handles packets with partial checksum. This 300 | /// “checksum offload” is a common feature on modern network cards. 301 | #[doc(alias = "VIRTIO_NET_F_CSUM")] 302 | const CSUM = 1 << 0; 303 | 304 | /// Driver handles packets with partial checksum. 305 | #[doc(alias = "VIRTIO_NET_F_GUEST_CSUM")] 306 | const GUEST_CSUM = 1 << 1; 307 | 308 | /// Control channel offloads 309 | /// reconfiguration support. 310 | #[doc(alias = "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS")] 311 | const CTRL_GUEST_OFFLOADS = 1 << 2; 312 | 313 | /// Device maximum MTU reporting is supported. If 314 | /// offered by the device, device advises driver about the value of 315 | /// its maximum MTU. If negotiated, the driver uses _mtu_ as 316 | /// the maximum MTU value. 317 | #[doc(alias = "VIRTIO_NET_F_MTU")] 318 | const MTU = 1 << 3; 319 | 320 | /// Device has given MAC address. 321 | #[doc(alias = "VIRTIO_NET_F_MAC")] 322 | const MAC = 1 << 5; 323 | 324 | /// Driver can receive TSOv4. 325 | #[doc(alias = "VIRTIO_NET_F_GUEST_TSO4")] 326 | const GUEST_TSO4 = 1 << 7; 327 | 328 | /// Driver can receive TSOv6. 329 | #[doc(alias = "VIRTIO_NET_F_GUEST_TSO6")] 330 | const GUEST_TSO6 = 1 << 8; 331 | 332 | /// Driver can receive TSO with ECN. 333 | #[doc(alias = "VIRTIO_NET_F_GUEST_ECN")] 334 | const GUEST_ECN = 1 << 9; 335 | 336 | /// Driver can receive UFO. 337 | #[doc(alias = "VIRTIO_NET_F_GUEST_UFO")] 338 | const GUEST_UFO = 1 << 10; 339 | 340 | /// Device can receive TSOv4. 341 | #[doc(alias = "VIRTIO_NET_F_HOST_TSO4")] 342 | const HOST_TSO4 = 1 << 11; 343 | 344 | /// Device can receive TSOv6. 345 | #[doc(alias = "VIRTIO_NET_F_HOST_TSO6")] 346 | const HOST_TSO6 = 1 << 12; 347 | 348 | /// Device can receive TSO with ECN. 349 | #[doc(alias = "VIRTIO_NET_F_HOST_ECN")] 350 | const HOST_ECN = 1 << 13; 351 | 352 | /// Device can receive UFO. 353 | #[doc(alias = "VIRTIO_NET_F_HOST_UFO")] 354 | const HOST_UFO = 1 << 14; 355 | 356 | /// Driver can merge receive buffers. 357 | #[doc(alias = "VIRTIO_NET_F_MRG_RXBUF")] 358 | const MRG_RXBUF = 1 << 15; 359 | 360 | /// Configuration status field is 361 | /// available. 362 | #[doc(alias = "VIRTIO_NET_F_STATUS")] 363 | const STATUS = 1 << 16; 364 | 365 | /// Control channel is available. 366 | #[doc(alias = "VIRTIO_NET_F_CTRL_VQ")] 367 | const CTRL_VQ = 1 << 17; 368 | 369 | /// Control channel RX mode support. 370 | #[doc(alias = "VIRTIO_NET_F_CTRL_RX")] 371 | const CTRL_RX = 1 << 18; 372 | 373 | /// Control channel VLAN filtering. 374 | #[doc(alias = "VIRTIO_NET_F_CTRL_VLAN")] 375 | const CTRL_VLAN = 1 << 19; 376 | 377 | /// Driver can send gratuitous 378 | /// packets. 379 | #[doc(alias = "VIRTIO_NET_F_GUEST_ANNOUNCE")] 380 | const GUEST_ANNOUNCE = 1 << 21; 381 | 382 | /// Device supports multiqueue with automatic 383 | /// receive steering. 384 | #[doc(alias = "VIRTIO_NET_F_MQ")] 385 | const MQ = 1 << 22; 386 | 387 | /// Set MAC address through control 388 | /// channel. 389 | #[doc(alias = "VIRTIO_NET_F_CTRL_MAC_ADDR")] 390 | const CTRL_MAC_ADDR = 1 << 23; 391 | 392 | /// Device can receive USO packets. Unlike UFO 393 | /// (fragmenting the packet) the USO splits large UDP packet 394 | /// to several segments when each of these smaller packets has UDP header. 395 | #[doc(alias = "VIRTIO_NET_F_HOST_USO")] 396 | const HOST_USO = 1 << 56; 397 | 398 | /// Device can report per-packet hash 399 | /// value and a type of calculated hash. 400 | #[doc(alias = "VIRTIO_NET_F_HASH_REPORT")] 401 | const HASH_REPORT = 1 << 57; 402 | 403 | /// Driver can provide the exact _hdr_len_ 404 | /// value. Device benefits from knowing the exact header length. 405 | #[doc(alias = "VIRTIO_NET_F_GUEST_HDRLEN")] 406 | const GUEST_HDRLEN = 1 << 59; 407 | 408 | /// Device supports RSS (receive-side scaling) 409 | /// with Toeplitz hash calculation and configurable hash 410 | /// parameters for receive steering. 411 | #[doc(alias = "VIRTIO_NET_F_RSS")] 412 | const RSS = 1 << 60; 413 | 414 | /// Device can process duplicated ACKs 415 | /// and report number of coalesced segments and duplicated ACKs. 416 | #[doc(alias = "VIRTIO_NET_F_RSC_EXT")] 417 | const RSC_EXT = 1 << 61; 418 | 419 | /// Device may act as a standby for a primary 420 | /// device with the same MAC address. 421 | #[doc(alias = "VIRTIO_NET_F_STANDBY")] 422 | const STANDBY = 1 << 62; 423 | 424 | /// Device reports speed and duplex. 425 | #[doc(alias = "VIRTIO_NET_F_SPEED_DUPLEX")] 426 | const SPEED_DUPLEX = 1 << 63; 427 | } 428 | } 429 | 430 | impl crate::FeatureBits for F { 431 | fn requirements(&self) -> Self { 432 | let mut requirements = Self::empty(); 433 | 434 | for feature in self.iter() { 435 | let requirement = match feature { 436 | Self::GUEST_TSO4 => Self::GUEST_CSUM, 437 | Self::GUEST_TSO6 => Self::GUEST_CSUM, 438 | Self::GUEST_ECN => Self::GUEST_TSO4 | Self::GUEST_TSO6, 439 | Self::GUEST_UFO => Self::GUEST_CSUM, 440 | Self::HOST_TSO4 => Self::CSUM, 441 | Self::HOST_TSO6 => Self::CSUM, 442 | Self::HOST_ECN => Self::HOST_TSO4 | Self::HOST_TSO6, 443 | Self::HOST_UFO => Self::CSUM, 444 | Self::HOST_USO => Self::CSUM, 445 | Self::CTRL_RX => Self::CTRL_VQ, 446 | Self::CTRL_VLAN => Self::CTRL_VQ, 447 | Self::GUEST_ANNOUNCE => Self::CTRL_VQ, 448 | Self::MQ => Self::CTRL_VQ, 449 | Self::CTRL_MAC_ADDR => Self::CTRL_VQ, 450 | Self::RSC_EXT => Self::HOST_TSO4 | Self::HOST_TSO6, 451 | Self::RSS => Self::CTRL_VQ, 452 | _ => Self::empty(), 453 | }; 454 | requirements.insert(requirement); 455 | } 456 | 457 | requirements 458 | } 459 | } 460 | } 461 | 462 | pub mod fs { 463 | use crate::le128; 464 | 465 | feature_bits! { 466 | /// File System Device Feature Bits 467 | #[doc(alias = "VIRTIO_FS_F")] 468 | pub struct F: le128 { 469 | /// Device has support for FUSE notify 470 | /// messages. The notification queue is virtqueue 1. 471 | #[doc(alias = "VIRTIO_FS_F_NOTIFICATION")] 472 | const NOTIFICATION = 1 << 0; 473 | } 474 | } 475 | 476 | impl crate::FeatureBits for F {} 477 | } 478 | 479 | pub mod vsock { 480 | use crate::le128; 481 | 482 | feature_bits! { 483 | /// Socket Device Feature Bits 484 | #[doc(alias = "VIRTIO_VSOCK_F")] 485 | pub struct F: le128 { 486 | /// stream socket type is supported. 487 | #[doc(alias = "VIRTIO_VSOCK_F_STREAM")] 488 | const STREAM = 1 << 0; 489 | 490 | /// seqpacket socket type is supported. 491 | #[doc(alias = "VIRTIO_VSOCK_F_SEQPACKET")] 492 | const SEQPACKET = 1 << 1; 493 | } 494 | } 495 | 496 | impl crate::FeatureBits for F {} 497 | } 498 | 499 | #[cfg(test)] 500 | mod tests { 501 | use super::*; 502 | 503 | #[rustfmt::skip] 504 | #[test] 505 | fn requirements_satisfied() { 506 | assert!(F::INDIRECT_DESC.requirements_satisfied()); 507 | 508 | assert!(net::F::CSUM.requirements_satisfied()); 509 | 510 | assert!(!net::F::MQ.requirements_satisfied()); 511 | assert!((net::F::MQ | net::F::CTRL_VQ).requirements_satisfied()); 512 | 513 | assert!(!net::F::HOST_TSO4.requirements_satisfied()); 514 | assert!((net::F::HOST_TSO4 | net::F::CSUM).requirements_satisfied()); 515 | assert!((net::F::HOST_TSO4 | net::F::HOST_TSO6 | net::F::CSUM).requirements_satisfied()); 516 | 517 | assert!(!net::F::HOST_ECN.requirements_satisfied()); 518 | assert!(!(net::F::HOST_ECN | net::F::CSUM).requirements_satisfied()); 519 | assert!(!(net::F::HOST_ECN | net::F::HOST_TSO4).requirements_satisfied()); 520 | assert!((net::F::HOST_ECN | net::F::HOST_TSO4 | net::F::CSUM).requirements_satisfied()); 521 | assert!((net::F::HOST_ECN | net::F::HOST_TSO4 | net::F::HOST_TSO6 | net::F::CSUM).requirements_satisfied()); 522 | } 523 | } 524 | -------------------------------------------------------------------------------- /src/fs.rs: -------------------------------------------------------------------------------- 1 | //! File System Device 2 | 3 | use volatile::access::ReadOnly; 4 | use volatile_macro::VolatileFieldAccess; 5 | 6 | pub use super::features::fs::F; 7 | use super::le32; 8 | 9 | /// Device configuration 10 | /// 11 | /// Use [`ConfigVolatileFieldAccess`] to work with this struct. 12 | #[doc(alias = "virtio_fs_config")] 13 | #[cfg_attr( 14 | feature = "zerocopy", 15 | derive( 16 | zerocopy_derive::KnownLayout, 17 | zerocopy_derive::Immutable, 18 | zerocopy_derive::FromBytes, 19 | ) 20 | )] 21 | #[derive(VolatileFieldAccess)] 22 | #[repr(C)] 23 | pub struct Config { 24 | /// This is the name associated with this file system. The tag is 25 | /// encoded in UTF-8 and padded with NUL bytes if shorter than the 26 | /// available space. This field is not NUL-terminated if the encoded bytes 27 | /// take up the entire field. 28 | #[access(ReadOnly)] 29 | tag: [u8; 36], 30 | 31 | /// This is the total number of request virtqueues 32 | /// exposed by the device. Each virtqueue offers identical functionality and 33 | /// there are no ordering guarantees between requests made available on 34 | /// different queues. Use of multiple queues is intended to increase 35 | /// performance. 36 | #[access(ReadOnly)] 37 | num_request_queues: le32, 38 | 39 | /// This is the minimum number of bytes required for each 40 | /// buffer in the notification queue. 41 | #[access(ReadOnly)] 42 | notify_buf_size: le32, 43 | } 44 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains the Rust equivalents of the definitions from the [Virtual I/O Device (VIRTIO) Specification](https://github.com/oasis-tcs/virtio-spec). 2 | //! This crate aims to be unopinionated regarding actual VIRTIO drivers that are implemented on top of this crate. 3 | //! 4 | //! # Usage 5 | //! 6 | //! We recommend to rename this crate to `virtio` when adding the dependency. 7 | //! This allows closely matching the specification when using definitions: 8 | //! 9 | //! - `VIRTIO_NET_F_CSUM` from the specification becomes [`virtio::net::F::CSUM`] in this crate. 10 | //! - `virtio_net_config` from the specification becomes [`virtio::net::Config`] in this crate. 11 | //! 12 | //! [`virtio::net::F::CSUM`]: net::F::CSUM 13 | //! [`virtio::net::Config`]: net::Config 14 | //! 15 | //! Either run 16 | //! 17 | //! ```bash 18 | //! cargo add virtio-spec --rename virtio 19 | //! ``` 20 | //! 21 | //! or manually edit your `Cargo.toml`: 22 | //! 23 | //! ```toml 24 | //! [dependencies] 25 | //! virtio = { package = "virtio-spec", version = "x.y.z" } 26 | //! ``` 27 | //! 28 | //! ## Features 29 | //! 30 | //! This crate has the following Cargo features: 31 | //! 32 | //! - `alloc` enables allocating unsized structs such as [`virtq::Avail`] and [`virtq::Used`] via the [`allocator_api2`] crate. 33 | //! - `mmio` enables the [`mmio`] module for Virtio Over MMIO. 34 | //! - `nightly` enables nightly-only functionality. 35 | //! - `pci` enables the [`pci`] module for Virtio Over PCI via the [`pci_types`] crate. 36 | //! - `zerocopy` derives the following traits for most structs: 37 | //! - [`zerocopy::KnownLayout`] 38 | //! - [`zerocopy::Immutable`] 39 | //! - [`zerocopy::FromBytes`] 40 | //! - [`zerocopy::IntoBytes`] 41 | //! 42 | //! # Implementation Status 43 | //! 44 | //! This crate adds new modules by demand. 45 | //! If you need anything that is not available yet, please open an issue. 46 | //! 47 | //! ## Virtqueues 48 | //! 49 | //! | Virtqueue | Available | Module | 50 | //! | ----------------- | --------- | ---------- | 51 | //! | Split Virtqueues | ✅ | [`virtq`] | 52 | //! | Packed Virtqueues | ✅ | [`pvirtq`] | 53 | //! 54 | //! ## Transport Options 55 | //! 56 | //! | Transport Option | Available | Module | 57 | //! | ---------------- | --------- | -------- | 58 | //! | PCI Bus | ✅ | [`pci`] | 59 | //! | MMIO | ✅ | [`mmio`] | 60 | //! | Channel I/O | ❌ | | 61 | //! 62 | //! ## Device Types 63 | //! 64 | //! | Device Type | Available | Module | 65 | //! | --------------------------------- | --------- | --------- | 66 | //! | Network Device | ✅ | [`net`] | 67 | //! | Block Device | ❌ | | 68 | //! | Console Device | ❌ | | 69 | //! | Entropy Device | ❌ | | 70 | //! | Traditional Memory Balloon Device | ❌ | | 71 | //! | SCSI Host Device | ❌ | | 72 | //! | GPU Device | ❌ | | 73 | //! | Input Device | ❌ | | 74 | //! | Crypto Device | ❌ | | 75 | //! | Socket Device | ✅ | [`vsock`] | 76 | //! | File System Device | ✅ | [`fs`] | 77 | //! | RPMB Device | ❌ | | 78 | //! | IOMMU Device | ❌ | | 79 | //! | Sound Device | ❌ | | 80 | //! | Memory Device | ❌ | | 81 | //! | I2C Adapter Device | ❌ | | 82 | //! | SCMI Device | ❌ | | 83 | //! | GPIO Device | ❌ | | 84 | //! | PMEM Device | ❌ | | 85 | 86 | #![cfg_attr(not(test), no_std)] 87 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 88 | #![cfg_attr(feature = "nightly", feature(allocator_api))] 89 | 90 | #[cfg(feature = "alloc")] 91 | extern crate alloc; 92 | 93 | #[macro_use] 94 | mod bitflags; 95 | #[macro_use] 96 | pub mod volatile; 97 | #[cfg(any(feature = "mmio", feature = "pci"))] 98 | mod driver_notifications; 99 | mod features; 100 | pub mod fs; 101 | #[cfg(feature = "mmio")] 102 | pub mod mmio; 103 | pub mod net; 104 | #[cfg(feature = "pci")] 105 | pub mod pci; 106 | pub mod pvirtq; 107 | pub mod virtq; 108 | pub mod vsock; 109 | 110 | pub use endian_num::{be128, be16, be32, be64, le128, le16, le32, le64, Be, Le}; 111 | use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive}; 112 | 113 | pub use self::features::{FeatureBits, F}; 114 | 115 | virtio_bitflags! { 116 | /// Device Status Field 117 | /// 118 | /// During device initialization by a driver, 119 | /// the driver follows the sequence of steps specified in 120 | /// _General Initialization And Device Operation / Device 121 | /// Initialization_. 122 | /// 123 | /// The `device status` field provides a simple low-level 124 | /// indication of the completed steps of this sequence. 125 | /// It's most useful to imagine it hooked up to traffic 126 | /// lights on the console indicating the status of each device. The 127 | /// following bits are defined (listed below in the order in which 128 | /// they would be typically set): 129 | pub struct DeviceStatus: u8 { 130 | /// Indicates that the guest OS has found the 131 | /// device and recognized it as a valid virtio device. 132 | const ACKNOWLEDGE = 1; 133 | 134 | /// Indicates that the guest OS knows how to drive the 135 | /// device. 136 | /// 137 | ///
138 | /// 139 | /// There could be a significant (or infinite) delay before setting 140 | /// this bit. For example, under Linux, drivers can be loadable modules. 141 | /// 142 | ///
143 | const DRIVER = 2; 144 | 145 | /// Indicates that something went wrong in the guest, 146 | /// and it has given up on the device. This could be an internal 147 | /// error, or the driver didn't like the device for some reason, or 148 | /// even a fatal error during device operation. 149 | const FAILED = 128; 150 | 151 | /// Indicates that the driver has acknowledged all the 152 | /// features it understands, and feature negotiation is complete. 153 | const FEATURES_OK = 8; 154 | 155 | /// Indicates that the driver is set up and ready to 156 | /// drive the device. 157 | const DRIVER_OK = 4; 158 | 159 | /// Indicates that the device has experienced 160 | /// an error from which it can't recover. 161 | const DEVICE_NEEDS_RESET = 64; 162 | } 163 | } 164 | 165 | /// Virtio Device IDs 166 | #[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 167 | #[non_exhaustive] 168 | #[repr(u8)] 169 | pub enum Id { 170 | /// reserved (invalid) 171 | Reserved = 0, 172 | 173 | /// network card 174 | Net = 1, 175 | 176 | /// block device 177 | Block = 2, 178 | 179 | /// console 180 | Console = 3, 181 | 182 | /// entropy source 183 | Rng = 4, 184 | 185 | /// memory ballooning (traditional) 186 | Balloon = 5, 187 | 188 | /// ioMemory 189 | IoMem = 6, 190 | 191 | /// rpmsg 192 | Rpmsg = 7, 193 | 194 | /// SCSI host 195 | Scsi = 8, 196 | 197 | /// 9P transport 198 | NineP = 9, 199 | 200 | /// mac80211 wlan 201 | Mac80211Wlan = 10, 202 | 203 | /// rproc serial 204 | RprocSerial = 11, 205 | 206 | /// virtio CAIF 207 | Caif = 12, 208 | 209 | /// memory balloon 210 | MemoryBalloon = 13, 211 | 212 | /// GPU device 213 | Gpu = 16, 214 | 215 | /// Timer/Clock device 216 | Clock = 17, 217 | 218 | /// Input device 219 | Input = 18, 220 | 221 | /// Socket device 222 | Vsock = 19, 223 | 224 | /// Crypto device 225 | Crypto = 20, 226 | 227 | /// Signal Distribution Module 228 | SignalDist = 21, 229 | 230 | /// pstore device 231 | Pstore = 22, 232 | 233 | /// IOMMU device 234 | Iommu = 23, 235 | 236 | /// Memory device 237 | Mem = 24, 238 | 239 | /// Audio device 240 | Sound = 25, 241 | 242 | /// file system device 243 | Fs = 26, 244 | 245 | /// PMEM device 246 | Pmem = 27, 247 | 248 | /// RPMB device 249 | Rpmb = 28, 250 | 251 | /// mac80211 hwsim wireless simulation device 252 | Mac80211Hwsim = 29, 253 | 254 | /// Video encoder device 255 | VideoEncoder = 30, 256 | 257 | /// Video decoder device 258 | VideoDecoder = 31, 259 | 260 | /// SCMI device 261 | Scmi = 32, 262 | 263 | /// NitroSecureModule 264 | NitroSecMod = 33, 265 | 266 | /// I2C adapter 267 | I2cAdapter = 34, 268 | 269 | /// Watchdog 270 | Watchdog = 35, 271 | 272 | /// CAN device 273 | Can = 36, 274 | 275 | /// Parameter Server 276 | ParamServ = 38, 277 | 278 | /// Audio policy device 279 | AudioPolicy = 39, 280 | 281 | /// Bluetooth device 282 | Bt = 40, 283 | 284 | /// GPIO device 285 | Gpio = 41, 286 | 287 | /// RDMA device 288 | Rdma = 42, 289 | 290 | /// Unknown device 291 | #[num_enum(catch_all)] 292 | Unknown(u8), 293 | } 294 | 295 | /// Descriptor Ring Change Event Flags 296 | #[doc(alias = "RING_EVENT_FLAGS")] 297 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 298 | #[non_exhaustive] 299 | #[repr(u8)] 300 | pub enum RingEventFlags { 301 | /// Enable events 302 | #[doc(alias = "RING_EVENT_FLAGS_ENABLE")] 303 | Enable = 0x0, 304 | 305 | /// Disable events 306 | #[doc(alias = "RING_EVENT_FLAGS_DISABLE")] 307 | Disable = 0x1, 308 | 309 | /// Enable events for a specific descriptor 310 | /// (as specified by Descriptor Ring Change Event Offset/Wrap Counter). 311 | /// Only valid if VIRTIO_F_EVENT_IDX has been negotiated. 312 | #[doc(alias = "RING_EVENT_FLAGS_DESC")] 313 | Desc = 0x2, 314 | 315 | Reserved = 0x3, 316 | } 317 | 318 | impl RingEventFlags { 319 | const fn from_bits(bits: u8) -> Self { 320 | match bits { 321 | 0x0 => Self::Enable, 322 | 0x1 => Self::Disable, 323 | 0x2 => Self::Desc, 324 | 0x3 => Self::Reserved, 325 | _ => unreachable!(), 326 | } 327 | } 328 | 329 | const fn into_bits(self) -> u8 { 330 | self as u8 331 | } 332 | } 333 | 334 | /// Common device configuration space functionality. 335 | pub trait DeviceConfigSpace: Sized { 336 | /// Read from device configuration space. 337 | /// 338 | /// This function should be used when reading from fields greater than 339 | /// 32 bits wide or when reading from multiple fields. 340 | /// 341 | /// As described in _Driver Requirements: Device Configuration Space_, 342 | /// this method checks the configuration atomicity value of the device 343 | /// and only returns once the value was the same before and after the 344 | /// provided function. 345 | /// 346 | /// # Examples 347 | /// 348 | /// ```rust 349 | /// # use virtio_spec as virtio; 350 | /// use virtio::net::ConfigVolatileFieldAccess; 351 | /// use virtio::DeviceConfigSpace; 352 | /// use volatile::access::ReadOnly; 353 | /// use volatile::VolatilePtr; 354 | /// 355 | /// fn read_mac( 356 | /// common_cfg: VolatilePtr<'_, virtio::pci::CommonCfg, ReadOnly>, 357 | /// net_cfg: VolatilePtr<'_, virtio::net::Config, ReadOnly>, 358 | /// ) -> [u8; 6] { 359 | /// common_cfg.read_config_with(|| net_cfg.mac().read()) 360 | /// } 361 | /// ``` 362 | fn read_config_with(self, f: F) -> T 363 | where 364 | F: FnMut() -> T; 365 | } 366 | -------------------------------------------------------------------------------- /src/mmio.rs: -------------------------------------------------------------------------------- 1 | //! Definitions for Virtio over MMIO. 2 | 3 | use core::mem; 4 | 5 | use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess, WriteOnly}; 6 | use volatile::VolatilePtr; 7 | 8 | pub use crate::driver_notifications::NotificationData; 9 | use crate::volatile::{OveralignedVolatilePtr, WideVolatilePtr}; 10 | use crate::{le16, le32, DeviceConfigSpace, DeviceStatus, Id}; 11 | 12 | /// MMIO Device Registers 13 | /// 14 | /// Use [`DeviceRegistersVolatileFieldAccess`] and [`DeviceRegistersVolatileWideFieldAccess`] to work with this struct. 15 | #[repr(transparent)] 16 | pub struct DeviceRegisters([le32; 0x100 / mem::size_of::()]); 17 | 18 | macro_rules! field_fn { 19 | ( 20 | $(#[doc = $doc:literal])* 21 | #[doc(alias = $alias:literal)] 22 | #[access($Access:ty)] 23 | $field:ident: le32, 24 | ) => { 25 | $(#[doc = $doc])* 26 | #[doc(alias = $alias)] 27 | fn $field(self) -> VolatilePtr<'a, le32, A::Restricted> 28 | where 29 | A: RestrictAccess<$Access>; 30 | }; 31 | ( 32 | $(#[doc = $doc:literal])* 33 | #[doc(alias = $alias:literal)] 34 | #[access($Access:ty)] 35 | $field:ident: (), 36 | ) => { 37 | $(#[doc = $doc])* 38 | #[doc(alias = $alias)] 39 | fn $field(self) -> VolatilePtr<'a, (), A::Restricted> 40 | where 41 | A: RestrictAccess<$Access>; 42 | }; 43 | ( 44 | $(#[doc = $doc:literal])* 45 | #[doc(alias = $alias:literal)] 46 | #[access($Access:ty)] 47 | $field:ident: $T:ty, 48 | ) => { 49 | $(#[doc = $doc])* 50 | #[doc(alias = $alias)] 51 | fn $field(self) -> OveralignedVolatilePtr<'a, $T, le32, A::Restricted> 52 | where 53 | A: RestrictAccess<$Access>; 54 | }; 55 | } 56 | 57 | macro_rules! field_impl { 58 | ( 59 | #[offset($offset:literal)] 60 | #[access($Access:ty)] 61 | $field:ident: le32, 62 | ) => { 63 | fn $field(self) -> VolatilePtr<'a, le32, A::Restricted> 64 | where 65 | A: RestrictAccess<$Access>, 66 | { 67 | unsafe { 68 | self.map(|ptr| ptr.cast::().byte_add($offset)) 69 | .restrict() 70 | } 71 | } 72 | }; 73 | ( 74 | #[offset($offset:literal)] 75 | #[access($Access:ty)] 76 | $field:ident: (), 77 | ) => { 78 | fn $field(self) -> VolatilePtr<'a, (), A::Restricted> 79 | where 80 | A: RestrictAccess<$Access>, 81 | { 82 | unsafe { 83 | self.map(|ptr| ptr.cast::<()>().byte_add($offset)) 84 | .restrict() 85 | } 86 | } 87 | }; 88 | ( 89 | #[offset($offset:literal)] 90 | #[access($Access:ty)] 91 | $field:ident: $T:ty, 92 | ) => { 93 | fn $field(self) -> OveralignedVolatilePtr<'a, $T, le32, A::Restricted> 94 | where 95 | A: RestrictAccess<$Access>, 96 | { 97 | let ptr = unsafe { self.map(|ptr| ptr.cast::().byte_add($offset)) }; 98 | OveralignedVolatilePtr::new(ptr.restrict()) 99 | } 100 | }; 101 | } 102 | 103 | macro_rules! device_register_impl { 104 | ( 105 | $(#[doc = $outer_doc:literal])* 106 | pub struct DeviceRegisters { 107 | $( 108 | $(#[doc = $doc:literal])* 109 | #[doc(alias = $alias:literal)] 110 | #[offset($offset:literal)] 111 | #[access($Access:ty)] 112 | $field:ident: $T:tt, 113 | )* 114 | } 115 | ) => { 116 | $(#[doc = $outer_doc])* 117 | pub trait DeviceRegistersVolatileFieldAccess<'a, A> { 118 | $( 119 | field_fn! { 120 | $(#[doc = $doc])* 121 | #[doc(alias = $alias)] 122 | #[access($Access)] 123 | $field: $T, 124 | } 125 | )* 126 | } 127 | 128 | impl<'a, A> DeviceRegistersVolatileFieldAccess<'a, A> for VolatilePtr<'a, DeviceRegisters, A> { 129 | $( 130 | field_impl! { 131 | #[offset($offset)] 132 | #[access($Access)] 133 | $field: $T, 134 | } 135 | )* 136 | } 137 | }; 138 | } 139 | 140 | device_register_impl! { 141 | /// MMIO Device Registers 142 | pub struct DeviceRegisters { 143 | /// Magic Value 144 | /// 145 | /// 0x74726976 146 | /// (a Little Endian equivalent of the “virt” string). 147 | #[doc(alias = "MagicValue")] 148 | #[offset(0x000)] 149 | #[access(ReadOnly)] 150 | magic_value: le32, 151 | 152 | /// Device version number 153 | /// 154 | /// 0x2. 155 | /// 156 | ///
157 | /// 158 | /// Legacy devices (see _Virtio Transport Options / Virtio Over MMIO / Legacy interface_) used 0x1. 159 | /// 160 | ///
161 | #[doc(alias = "Version")] 162 | #[offset(0x004)] 163 | #[access(ReadOnly)] 164 | version: le32, 165 | 166 | /// Virtio Subsystem Device ID 167 | /// 168 | /// See _Device Types_ for possible values. 169 | /// Value zero (0x0) is used to 170 | /// define a system memory map with placeholder devices at static, 171 | /// well known addresses, assigning functions to them depending 172 | /// on user's needs. 173 | #[doc(alias = "DeviceID")] 174 | #[offset(0x008)] 175 | #[access(ReadOnly)] 176 | device_id: Id, 177 | 178 | /// Virtio Subsystem Vendor ID 179 | #[doc(alias = "VendorID")] 180 | #[offset(0x00c)] 181 | #[access(ReadOnly)] 182 | vendor_id: le32, 183 | 184 | /// Flags representing features the device supports 185 | /// 186 | /// Reading from this register returns 32 consecutive flag bits, 187 | /// the least significant bit depending on the last value written to 188 | /// `DeviceFeaturesSel`. Access to this register returns 189 | /// bits `DeviceFeaturesSel`*32 to (`DeviceFeaturesSel`*32)+31, eg. 190 | /// feature bits 0 to 31 if `DeviceFeaturesSel` is set to 0 and 191 | /// features bits 32 to 63 if `DeviceFeaturesSel` is set to 1. 192 | /// Also see _Basic Facilities of a Virtio Device / Feature Bits_. 193 | #[doc(alias = "DeviceFeatures")] 194 | #[offset(0x010)] 195 | #[access(ReadOnly)] 196 | device_features: le32, 197 | 198 | /// Device (host) features word selection. 199 | /// 200 | /// Writing to this register selects a set of 32 device feature bits 201 | /// accessible by reading from `DeviceFeatures`. 202 | #[doc(alias = "DeviceFeaturesSel")] 203 | #[offset(0x014)] 204 | #[access(WriteOnly)] 205 | device_features_sel: le32, 206 | 207 | /// Flags representing device features understood and activated by the driver 208 | /// 209 | /// Writing to this register sets 32 consecutive flag bits, the least significant 210 | /// bit depending on the last value written to `DriverFeaturesSel`. 211 | /// Access to this register sets bits `DriverFeaturesSel`*32 212 | /// to (`DriverFeaturesSel`*32)+31, eg. feature bits 0 to 31 if 213 | /// `DriverFeaturesSel` is set to 0 and features bits 32 to 63 if 214 | /// `DriverFeaturesSel` is set to 1. Also see _Basic Facilities of a Virtio Device / Feature Bits_. 215 | #[doc(alias = "DriverFeatures")] 216 | #[offset(0x020)] 217 | #[access(WriteOnly)] 218 | driver_features: le32, 219 | 220 | /// Activated (guest) features word selection 221 | /// 222 | /// Writing to this register selects a set of 32 activated feature 223 | /// bits accessible by writing to `DriverFeatures`. 224 | #[doc(alias = "DriverFeaturesSel")] 225 | #[offset(0x024)] 226 | #[access(WriteOnly)] 227 | driver_features_sel: le32, 228 | 229 | /// Virtual queue index 230 | /// 231 | /// Writing to this register selects the virtual queue that the 232 | /// following operations on `QueueNumMax`, `QueueNum`, `QueueReady`, 233 | /// `QueueDescLow`, `QueueDescHigh`, `QueueDriverlLow`, `QueueDriverHigh`, 234 | /// `QueueDeviceLow`, `QueueDeviceHigh` and `QueueReset` apply to. The index 235 | /// number of the first queue is zero (0x0). 236 | #[doc(alias = "QueueSel")] 237 | #[offset(0x030)] 238 | #[access(WriteOnly)] 239 | queue_sel: le16, 240 | 241 | /// Maximum virtual queue size 242 | /// 243 | /// Reading from the register returns the maximum size (number of 244 | /// elements) of the queue the device is ready to process or 245 | /// zero (0x0) if the queue is not available. This applies to the 246 | /// queue selected by writing to `QueueSel`. 247 | #[doc(alias = "QueueNumMax")] 248 | #[offset(0x034)] 249 | #[access(ReadOnly)] 250 | queue_num_max: le16, 251 | 252 | /// Virtual queue size 253 | /// 254 | /// Queue size is the number of elements in the queue. 255 | /// Writing to this register notifies the device what size of the 256 | /// queue the driver will use. This applies to the queue selected by 257 | /// writing to `QueueSel`. 258 | #[doc(alias = "QueueNum")] 259 | #[offset(0x038)] 260 | #[access(WriteOnly)] 261 | queue_num: le16, 262 | 263 | /// Virtual queue ready bit 264 | /// 265 | /// Writing one (0x1) to this register notifies the device that it can 266 | /// execute requests from this virtual queue. Reading from this register 267 | /// returns the last value written to it. Both read and write 268 | /// accesses apply to the queue selected by writing to `QueueSel`. 269 | #[doc(alias = "QueueReady")] 270 | #[offset(0x044)] 271 | #[access(ReadWrite)] 272 | queue_ready: bool, 273 | 274 | /// Queue notifier 275 | /// 276 | /// Writing a value to this register notifies the device that 277 | /// there are new buffers to process in a queue. 278 | /// 279 | /// When VIRTIO_F_NOTIFICATION_DATA has not been negotiated, 280 | /// the value written is the queue index. 281 | /// 282 | /// When VIRTIO_F_NOTIFICATION_DATA has been negotiated, 283 | /// the `Notification data` value has the following format: 284 | /// 285 | /// ```c 286 | /// le32 { 287 | /// vqn : 16; 288 | /// next_off : 15; 289 | /// next_wrap : 1; 290 | /// }; 291 | /// ``` 292 | /// 293 | /// See _Virtqueues / Driver notifications_ 294 | /// for the definition of the components. 295 | #[doc(alias = "QueueNotify")] 296 | #[offset(0x050)] 297 | #[access(WriteOnly)] 298 | queue_notify: le32, 299 | 300 | /// Interrupt status 301 | /// 302 | /// Reading from this register returns a bit mask of events that 303 | /// caused the device interrupt to be asserted. 304 | #[doc(alias = "InterruptStatus")] 305 | #[offset(0x060)] 306 | #[access(ReadOnly)] 307 | interrupt_status: InterruptStatus, 308 | 309 | /// Interrupt acknowledge 310 | /// 311 | /// Writing a value with bits set as defined in `InterruptStatus` 312 | /// to this register notifies the device that events causing 313 | /// the interrupt have been handled. 314 | #[doc(alias = "InterruptACK")] 315 | #[offset(0x064)] 316 | #[access(WriteOnly)] 317 | interrupt_ack: InterruptStatus, 318 | 319 | /// Device status 320 | /// 321 | /// Reading from this register returns the current device status 322 | /// flags. 323 | /// Writing non-zero values to this register sets the status flags, 324 | /// indicating the driver progress. Writing zero (0x0) to this 325 | /// register triggers a device reset. 326 | /// See also p. _Virtio Transport Options / Virtio Over MMIO / MMIO-specific Initialization And Device Operation / Device Initialization_. 327 | #[doc(alias = "Status")] 328 | #[offset(0x070)] 329 | #[access(ReadWrite)] 330 | status: DeviceStatus, 331 | 332 | /// Virtual queue's Descriptor Area 64 bit long physical address 333 | /// 334 | /// Writing to these two registers (lower 32 bits of the address 335 | /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies 336 | /// the device about location of the Descriptor Area of the queue 337 | /// selected by writing to `QueueSel` register. 338 | #[doc(alias = "QueueDescLow")] 339 | #[offset(0x080)] 340 | #[access(WriteOnly)] 341 | queue_desc_low: le32, 342 | 343 | /// Virtual queue's Descriptor Area 64 bit long physical address 344 | /// 345 | /// Writing to these two registers (lower 32 bits of the address 346 | /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies 347 | /// the device about location of the Descriptor Area of the queue 348 | /// selected by writing to `QueueSel` register. 349 | #[doc(alias = "QueueDescHigh")] 350 | #[offset(0x084)] 351 | #[access(WriteOnly)] 352 | queue_desc_high: le32, 353 | 354 | /// Virtual queue's Driver Area 64 bit long physical address 355 | /// 356 | /// Writing to these two registers (lower 32 bits of the address 357 | /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies 358 | /// the device about location of the Driver Area of the queue 359 | /// selected by writing to `QueueSel`. 360 | #[doc(alias = "QueueDriverLow")] 361 | #[offset(0x090)] 362 | #[access(WriteOnly)] 363 | queue_driver_low: le32, 364 | 365 | /// Virtual queue's Driver Area 64 bit long physical address 366 | /// 367 | /// Writing to these two registers (lower 32 bits of the address 368 | /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies 369 | /// the device about location of the Driver Area of the queue 370 | /// selected by writing to `QueueSel`. 371 | #[doc(alias = "QueueDriverHigh")] 372 | #[offset(0x094)] 373 | #[access(WriteOnly)] 374 | queue_driver_high: le32, 375 | 376 | /// Virtual queue's Device Area 64 bit long physical address 377 | /// 378 | /// Writing to these two registers (lower 32 bits of the address 379 | /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies 380 | /// the device about location of the Device Area of the queue 381 | /// selected by writing to `QueueSel`. 382 | #[doc(alias = "QueueDeviceLow")] 383 | #[offset(0x0a0)] 384 | #[access(WriteOnly)] 385 | queue_device_low: le32, 386 | 387 | /// Virtual queue's Device Area 64 bit long physical address 388 | /// 389 | /// Writing to these two registers (lower 32 bits of the address 390 | /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies 391 | /// the device about location of the Device Area of the queue 392 | /// selected by writing to `QueueSel`. 393 | #[doc(alias = "QueueDeviceHigh")] 394 | #[offset(0x0a4)] 395 | #[access(WriteOnly)] 396 | queue_device_high: le32, 397 | 398 | /// Shared memory id 399 | /// 400 | /// Writing to this register selects the shared memory region _Basic Facilities of a Virtio Device / Shared Memory Regions_ 401 | /// following operations on `SHMLenLow`, `SHMLenHigh`, 402 | /// `SHMBaseLow` and `SHMBaseHigh` apply to. 403 | #[doc(alias = "SHMSel")] 404 | #[offset(0x0ac)] 405 | #[access(WriteOnly)] 406 | shm_sel: le32, 407 | 408 | /// Shared memory region 64 bit long length 409 | /// 410 | /// These registers return the length of the shared memory 411 | /// region in bytes, as defined by the device for the region selected by 412 | /// the `SHMSel` register. The lower 32 bits of the length 413 | /// are read from `SHMLenLow` and the higher 32 bits from 414 | /// `SHMLenHigh`. Reading from a non-existent 415 | /// region (i.e. where the ID written to `SHMSel` is unused) 416 | /// results in a length of -1. 417 | #[doc(alias = "SHMLenLow")] 418 | #[offset(0x0b0)] 419 | #[access(ReadOnly)] 420 | shm_len_low: le32, 421 | 422 | /// Shared memory region 64 bit long length 423 | /// 424 | /// These registers return the length of the shared memory 425 | /// region in bytes, as defined by the device for the region selected by 426 | /// the `SHMSel` register. The lower 32 bits of the length 427 | /// are read from `SHMLenLow` and the higher 32 bits from 428 | /// `SHMLenHigh`. Reading from a non-existent 429 | /// region (i.e. where the ID written to `SHMSel` is unused) 430 | /// results in a length of -1. 431 | #[doc(alias = "SHMLenHigh")] 432 | #[offset(0x0b4)] 433 | #[access(ReadOnly)] 434 | shm_len_high: le32, 435 | 436 | /// Shared memory region 64 bit long physical address 437 | /// 438 | /// The driver reads these registers to discover the base address 439 | /// of the region in physical address space. This address is 440 | /// chosen by the device (or other part of the VMM). 441 | /// The lower 32 bits of the address are read from `SHMBaseLow` 442 | /// with the higher 32 bits from `SHMBaseHigh`. Reading 443 | /// from a non-existent region (i.e. where the ID written to 444 | /// `SHMSel` is unused) results in a base address of 445 | /// 0xffffffffffffffff. 446 | #[doc(alias = "SHMBaseLow")] 447 | #[offset(0x0b8)] 448 | #[access(ReadOnly)] 449 | shm_base_low: le32, 450 | 451 | /// Shared memory region 64 bit long physical address 452 | /// 453 | /// The driver reads these registers to discover the base address 454 | /// of the region in physical address space. This address is 455 | /// chosen by the device (or other part of the VMM). 456 | /// The lower 32 bits of the address are read from `SHMBaseLow` 457 | /// with the higher 32 bits from `SHMBaseHigh`. Reading 458 | /// from a non-existent region (i.e. where the ID written to 459 | /// `SHMSel` is unused) results in a base address of 460 | /// 0xffffffffffffffff. 461 | #[doc(alias = "SHMBaseHigh")] 462 | #[offset(0x0bc)] 463 | #[access(ReadOnly)] 464 | shm_base_high: le32, 465 | 466 | /// Virtual queue reset bit 467 | /// 468 | /// If VIRTIO_F_RING_RESET has been negotiated, writing one (0x1) to this 469 | /// register selectively resets the queue. Both read and write accesses 470 | /// apply to the queue selected by writing to `QueueSel`. 471 | #[doc(alias = "QueueReset")] 472 | #[offset(0x0c0)] 473 | #[access(ReadWrite)] 474 | queue_reset: le32, 475 | 476 | /// Configuration atomicity value 477 | /// 478 | /// Reading from this register returns a value describing a version of the device-specific configuration space (see `Config`). 479 | /// The driver can then access the configuration space and, when finished, read `ConfigGeneration` again. 480 | /// If no part of the configuration space has changed between these two `ConfigGeneration` reads, the returned values are identical. 481 | /// If the values are different, the configuration space accesses were not atomic and the driver has to perform the operations again. 482 | /// See also _Basic Facilities of a Virtio Device / Device Configuration Space_. 483 | #[doc(alias = "ConfigGeneration")] 484 | #[offset(0x0fc)] 485 | #[access(ReadOnly)] 486 | config_generation: le32, 487 | 488 | /// Configuration space 489 | /// 490 | /// Device-specific configuration space starts at the offset 0x100 491 | /// and is accessed with byte alignment. Its meaning and size 492 | /// depend on the device and the driver. 493 | #[doc(alias = "Config")] 494 | #[offset(0x100)] 495 | #[access(ReadWrite)] 496 | config: (), 497 | } 498 | } 499 | 500 | impl_wide_field_access! { 501 | /// MMIO Device Registers 502 | pub trait DeviceRegistersVolatileWideFieldAccess<'a, A>: DeviceRegisters { 503 | /// Virtual queue's Descriptor Area 64 bit long physical address 504 | /// 505 | /// Writing to these two registers (lower 32 bits of the address 506 | /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies 507 | /// the device about location of the Descriptor Area of the queue 508 | /// selected by writing to `QueueSel` register. 509 | #[doc(alias = "QueueDesc")] 510 | #[access(WriteOnly)] 511 | queue_desc: queue_desc_low, queue_desc_high; 512 | 513 | /// Virtual queue's Driver Area 64 bit long physical address 514 | /// 515 | /// Writing to these two registers (lower 32 bits of the address 516 | /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies 517 | /// the device about location of the Driver Area of the queue 518 | /// selected by writing to `QueueSel`. 519 | #[doc(alias = "QueueDriver")] 520 | #[access(WriteOnly)] 521 | queue_driver: queue_driver_low, queue_driver_high; 522 | 523 | /// Virtual queue's Device Area 64 bit long physical address 524 | /// 525 | /// Writing to these two registers (lower 32 bits of the address 526 | /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies 527 | /// the device about location of the Device Area of the queue 528 | /// selected by writing to `QueueSel`. 529 | #[doc(alias = "QueueDevice")] 530 | #[access(WriteOnly)] 531 | queue_device: queue_device_low, queue_device_high; 532 | 533 | /// Shared memory region 64 bit long length 534 | /// 535 | /// These registers return the length of the shared memory 536 | /// region in bytes, as defined by the device for the region selected by 537 | /// the `SHMSel` register. The lower 32 bits of the length 538 | /// are read from `SHMLenLow` and the higher 32 bits from 539 | /// `SHMLenHigh`. Reading from a non-existent 540 | /// region (i.e. where the ID written to `SHMSel` is unused) 541 | /// results in a length of -1. 542 | #[doc(alias = "SHMLen")] 543 | #[access(ReadOnly)] 544 | shm_len: shm_len_low, shm_len_high; 545 | 546 | /// Shared memory region 64 bit long physical address 547 | /// 548 | /// The driver reads these registers to discover the base address 549 | /// of the region in physical address space. This address is 550 | /// chosen by the device (or other part of the VMM). 551 | /// The lower 32 bits of the address are read from `SHMBaseLow` 552 | /// with the higher 32 bits from `SHMBaseHigh`. Reading 553 | /// from a non-existent region (i.e. where the ID written to 554 | /// `SHMSel` is unused) results in a base address of 555 | /// 0xffffffffffffffff. 556 | #[doc(alias = "SHMBase")] 557 | #[access(ReadOnly)] 558 | shm_base: shm_base_low, shm_base_high; 559 | } 560 | } 561 | 562 | impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, DeviceRegisters, A> 563 | where 564 | A: RestrictAccess, 565 | A::Restricted: Readable, 566 | { 567 | fn read_config_with(self, f: F) -> T 568 | where 569 | F: FnMut() -> T, 570 | { 571 | let mut f = f; 572 | loop { 573 | let before = self.config_generation().read(); 574 | let read = f(); 575 | let after = self.config_generation().read(); 576 | if after == before { 577 | break read; 578 | } 579 | } 580 | } 581 | } 582 | 583 | virtio_bitflags! { 584 | /// Interrupt Status 585 | pub struct InterruptStatus: u8 { 586 | /// Used Buffer Notification 587 | /// 588 | /// The interrupt was asserted because the device has used a buffer in at least one of the active virtual queues. 589 | const USED_BUFFER_NOTIFICATION = 1 << 0; 590 | 591 | /// Configuration Change Notification 592 | /// 593 | /// The interrupt was asserted because the configuration of the device has changed. 594 | const CONFIGURATION_CHANGE_NOTIFICATION = 1 << 1; 595 | } 596 | } 597 | -------------------------------------------------------------------------------- /src/net.rs: -------------------------------------------------------------------------------- 1 | //! Network Device 2 | 3 | use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive}; 4 | use volatile::access::ReadOnly; 5 | use volatile_macro::VolatileFieldAccess; 6 | 7 | pub use super::features::net::F; 8 | use crate::{le16, le32}; 9 | 10 | endian_bitflags! { 11 | /// Network Device Status Flags 12 | #[doc(alias = "VIRTIO_NET_S")] 13 | pub struct S: le16 { 14 | #[doc(alias = "VIRTIO_NET_S_LINK_UP")] 15 | const LINK_UP = 1; 16 | 17 | #[doc(alias = "VIRTIO_NET_S_ANNOUNCE")] 18 | const ANNOUNCE = 2; 19 | } 20 | } 21 | 22 | /// Network Device Configuration Layout 23 | /// 24 | /// Use [`ConfigVolatileFieldAccess`] to work with this struct. 25 | #[doc(alias = "virtio_net_config")] 26 | #[cfg_attr( 27 | feature = "zerocopy", 28 | derive( 29 | zerocopy_derive::KnownLayout, 30 | zerocopy_derive::Immutable, 31 | zerocopy_derive::FromBytes, 32 | ) 33 | )] 34 | #[derive(VolatileFieldAccess)] 35 | #[repr(C)] 36 | pub struct Config { 37 | #[access(ReadOnly)] 38 | mac: [u8; 6], 39 | 40 | #[access(ReadOnly)] 41 | status: S, 42 | 43 | #[access(ReadOnly)] 44 | max_virtqueue_pairs: le16, 45 | 46 | #[access(ReadOnly)] 47 | mtu: le16, 48 | 49 | #[access(ReadOnly)] 50 | speed: le32, 51 | 52 | #[access(ReadOnly)] 53 | duplex: u8, 54 | 55 | #[access(ReadOnly)] 56 | rss_max_key_size: u8, 57 | 58 | #[access(ReadOnly)] 59 | rss_max_indirection_table_length: le16, 60 | 61 | #[access(ReadOnly)] 62 | supported_hash_types: le32, 63 | } 64 | 65 | virtio_bitflags! { 66 | /// Network Device Header Flags 67 | #[doc(alias = "VIRTIO_NET_HDR_F")] 68 | pub struct HdrF: u8 { 69 | #[doc(alias = "VIRTIO_NET_HDR_F_NEEDS_CSUM")] 70 | const NEEDS_CSUM = 1; 71 | 72 | #[doc(alias = "VIRTIO_NET_HDR_F_DATA_VALID")] 73 | const DATA_VALID = 2; 74 | 75 | #[doc(alias = "VIRTIO_NET_HDR_F_RSC_INFO")] 76 | const RSC_INFO = 4; 77 | } 78 | } 79 | 80 | virtio_bitflags! { 81 | /// Network Device Header GSO Type 82 | #[doc(alias = "VIRTIO_NET_HDR_GSO")] 83 | pub struct HdrGso: u8 { 84 | #[doc(alias = "VIRTIO_NET_HDR_GSO_NONE")] 85 | const NONE = 0; 86 | 87 | #[doc(alias = "VIRTIO_NET_HDR_GSO_TCPV4")] 88 | const TCPV4 = 1; 89 | 90 | #[doc(alias = "VIRTIO_NET_HDR_GSO_UDP")] 91 | const UDP = 3; 92 | 93 | #[doc(alias = "VIRTIO_NET_HDR_GSO_TCPV6")] 94 | const TCPV6 = 4; 95 | 96 | #[doc(alias = "VIRTIO_NET_HDR_GSO_UDP_L4")] 97 | const UDP_L4 = 5; 98 | 99 | #[doc(alias = "VIRTIO_NET_HDR_GSO_ECN")] 100 | const ECN = 0x80; 101 | } 102 | } 103 | 104 | /// Network Device Header 105 | #[doc(alias = "virtio_net_hdr")] 106 | #[cfg_attr( 107 | feature = "zerocopy", 108 | derive( 109 | zerocopy_derive::KnownLayout, 110 | zerocopy_derive::Immutable, 111 | zerocopy_derive::FromBytes, 112 | zerocopy_derive::IntoBytes, 113 | ) 114 | )] 115 | #[derive(Default, Clone, Copy, Debug)] 116 | #[repr(C)] 117 | pub struct Hdr { 118 | pub flags: HdrF, 119 | pub gso_type: HdrGso, 120 | pub hdr_len: le16, 121 | pub gso_size: le16, 122 | pub csum_start: le16, 123 | pub csum_offset: le16, 124 | pub num_buffers: le16, 125 | } 126 | 127 | /// Network Device Header Hash Report 128 | /// 129 | /// Only if VIRTIO_NET_F_HASH_REPORT negotiated 130 | #[doc(alias = "virtio_net_hdr")] 131 | #[cfg_attr( 132 | feature = "zerocopy", 133 | derive( 134 | zerocopy_derive::KnownLayout, 135 | zerocopy_derive::Immutable, 136 | zerocopy_derive::FromBytes, 137 | zerocopy_derive::IntoBytes, 138 | ) 139 | )] 140 | #[derive(Default, Clone, Copy, Debug)] 141 | #[repr(C)] 142 | pub struct HdrHashReport { 143 | /// Only if VIRTIO_NET_F_HASH_REPORT negotiated 144 | pub hash_value: le32, 145 | /// Only if VIRTIO_NET_F_HASH_REPORT negotiated 146 | pub hash_report: le16, 147 | /// Only if VIRTIO_NET_F_HASH_REPORT negotiated 148 | pub padding_reserved: le16, 149 | } 150 | 151 | endian_bitflags! { 152 | /// Hash Type 153 | #[doc(alias = "VIRTIO_NET_HASH_TYPE")] 154 | pub struct HashType: le32 { 155 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_IPv4")] 156 | const IPV4 = 1 << 0; 157 | 158 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_TCPv4")] 159 | const TCPV4 = 1 << 1; 160 | 161 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_UDPv4")] 162 | const UDPV4 = 1 << 2; 163 | 164 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_IPv6")] 165 | const IPV6 = 1 << 3; 166 | 167 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_TCPv6")] 168 | const TCPV6 = 1 << 4; 169 | 170 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_UDPv6")] 171 | const UDPV6 = 1 << 5; 172 | 173 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_IP_EX")] 174 | const IP_EX = 1 << 6; 175 | 176 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_TCP_EX")] 177 | const TCP_EX = 1 << 7; 178 | 179 | #[doc(alias = "VIRTIO_NET_HASH_TYPE_UDP_EX")] 180 | const UDP_EX = 1 << 8; 181 | } 182 | } 183 | 184 | /// Hash Report 185 | #[doc(alias = "VIRTIO_NET_HASH_REPORT")] 186 | #[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 187 | #[non_exhaustive] 188 | #[repr(u16)] 189 | pub enum HashReport { 190 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_NONE")] 191 | None = 0, 192 | 193 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_IPv4")] 194 | Ipv4 = 1, 195 | 196 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_TCPv4")] 197 | Tcpv4 = 2, 198 | 199 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_UDPv4")] 200 | Udpv4 = 3, 201 | 202 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_IPv6")] 203 | IPv6 = 4, 204 | 205 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_TCPv6")] 206 | Tcpv6 = 5, 207 | 208 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_UDPv6")] 209 | Udpv6 = 6, 210 | 211 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_IPv6_EX")] 212 | Ipv6Ex = 7, 213 | 214 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_TCPv6_EX")] 215 | Tcpv6Ex = 8, 216 | 217 | #[doc(alias = "VIRTIO_NET_HASH_REPORT_UDPv6_EX")] 218 | Udpv6Ex = 9, 219 | 220 | #[num_enum(catch_all)] 221 | Unknown(u16), 222 | } 223 | 224 | /// Command class 225 | #[doc(alias = "VIRTIO_NET_CTRL")] 226 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 227 | #[non_exhaustive] 228 | #[repr(u8)] 229 | pub enum Ctrl { 230 | #[doc(alias = "VIRTIO_NET_CTRL_RX")] 231 | Rx = 0, 232 | 233 | #[doc(alias = "VIRTIO_NET_CTRL_MAC")] 234 | Mac = 1, 235 | 236 | #[doc(alias = "VIRTIO_NET_CTRL_VLAN")] 237 | Vlan = 2, 238 | 239 | #[doc(alias = "VIRTIO_NET_CTRL_ANNOUNCE")] 240 | Announce = 3, 241 | 242 | #[doc(alias = "VIRTIO_NET_CTRL_MQ")] 243 | Mq = 4, 244 | 245 | #[doc(alias = "VIRTIO_NET_CTRL_GUEST_OFFLOADS")] 246 | GuestOffloads = 5, 247 | } 248 | 249 | /// Commands 250 | pub mod ctrl { 251 | use num_enum::{IntoPrimitive, TryFromPrimitive}; 252 | 253 | /// Packed Receive Filtering commands 254 | #[doc(alias = "VIRTIO_NET_CTRL_RX")] 255 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 256 | #[non_exhaustive] 257 | #[repr(u8)] 258 | pub enum Rx { 259 | #[doc(alias = "VIRTIO_NET_CTRL_RX_PROMISC")] 260 | Promisc = 0, 261 | 262 | #[doc(alias = "VIRTIO_NET_CTRL_RX_ALLMULTI")] 263 | Allmulti = 1, 264 | 265 | #[doc(alias = "VIRTIO_NET_CTRL_RX_ALLUNI")] 266 | Alluni = 2, 267 | 268 | #[doc(alias = "VIRTIO_NET_CTRL_RX_NOMULTI")] 269 | Nomulti = 3, 270 | 271 | #[doc(alias = "VIRTIO_NET_CTRL_RX_NOUNI")] 272 | Nouni = 4, 273 | 274 | #[doc(alias = "VIRTIO_NET_CTRL_RX_NOBCAST")] 275 | Nobcast = 5, 276 | } 277 | 278 | /// MAC Address Filtering commands 279 | #[doc(alias = "VIRTIO_NET_CTRL_MAC")] 280 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 281 | #[non_exhaustive] 282 | #[repr(u8)] 283 | pub enum Mac { 284 | #[doc(alias = "VIRTIO_NET_CTRL_MAC_TABLE_SET")] 285 | TableSet = 0, 286 | 287 | #[doc(alias = "VIRTIO_NET_CTRL_MAC_ADDR_SET")] 288 | AddrSet = 1, 289 | } 290 | 291 | /// VLAN filtering commands 292 | #[doc(alias = "VIRTIO_NET_CTRL_VLAN")] 293 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 294 | #[non_exhaustive] 295 | #[repr(u8)] 296 | pub enum Vlan { 297 | #[doc(alias = "VIRTIO_NET_CTRL_VLAN_ADD")] 298 | Add = 0, 299 | 300 | #[doc(alias = "VIRTIO_NET_CTRL_VLAN_DEL")] 301 | Del = 1, 302 | } 303 | 304 | /// Gratuitous Packet Sending commands 305 | #[doc(alias = "VIRTIO_NET_CTRL_ANNOUNCE")] 306 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 307 | #[non_exhaustive] 308 | #[repr(u8)] 309 | pub enum Announce { 310 | #[doc(alias = "VIRTIO_NET_CTRL_ANNOUNCE_ACK")] 311 | Ack = 0, 312 | } 313 | 314 | /// Multiqueue mode commands 315 | #[doc(alias = "VIRTIO_NET_CTRL_MQ")] 316 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 317 | #[non_exhaustive] 318 | #[repr(u8)] 319 | pub enum Mq { 320 | /// For automatic receive steering 321 | #[doc(alias = "VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET")] 322 | VqPairsSet = 0, 323 | 324 | /// For configurable receive steering 325 | #[doc(alias = "VIRTIO_NET_CTRL_MQ_RSS_CONFIG")] 326 | RssConfig = 1, 327 | 328 | /// For configurable hash calculation 329 | #[doc(alias = "VIRTIO_NET_CTRL_MQ_HASH_CONFIG")] 330 | HashConfig = 2, 331 | } 332 | 333 | /// Setting Offloads State commands 334 | #[doc(alias = "VIRTIO_NET_CTRL_GUEST_OFFLOADS")] 335 | #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 336 | #[non_exhaustive] 337 | #[repr(u8)] 338 | pub enum GuestOffloads { 339 | #[doc(alias = "VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET")] 340 | Set = 0, 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /src/pci.rs: -------------------------------------------------------------------------------- 1 | //! Definitions for Virtio over PCI bus. 2 | 3 | use core::mem; 4 | 5 | use num_enum::{FromPrimitive, IntoPrimitive}; 6 | use pci_types::capability::PciCapabilityAddress; 7 | use pci_types::ConfigRegionAccess; 8 | use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess}; 9 | use volatile::VolatilePtr; 10 | use volatile_macro::VolatileFieldAccess; 11 | 12 | pub use crate::driver_notifications::NotificationData; 13 | use crate::volatile::WideVolatilePtr; 14 | use crate::{le16, le32, le64, DeviceConfigSpace, DeviceStatus, Le}; 15 | 16 | /// PCI Capability 17 | /// 18 | /// See [`CapData`] for reading additional fields. 19 | #[doc(alias = "virtio_pci_cap")] 20 | #[cfg_attr( 21 | feature = "zerocopy", 22 | derive( 23 | zerocopy_derive::KnownLayout, 24 | zerocopy_derive::Immutable, 25 | zerocopy_derive::FromBytes, 26 | ) 27 | )] 28 | #[derive(Clone, Copy, Debug)] 29 | #[repr(C)] 30 | pub struct Cap { 31 | /// Generic PCI field: `PCI_CAP_ID_VNDR` 32 | /// 33 | /// 0x09; Identifies a vendor-specific capability. 34 | pub cap_vndr: u8, 35 | 36 | /// Generic PCI field: next ptr. 37 | /// 38 | /// Link to next capability in the capability list in the PCI configuration space. 39 | pub cap_next: u8, 40 | 41 | /// Generic PCI field: capability length 42 | /// 43 | /// Length of this capability structure, including the whole of 44 | /// struct virtio_pci_cap, and extra data if any. 45 | /// This length MAY include padding, or fields unused by the driver. 46 | pub cap_len: u8, 47 | 48 | /// Identifies the structure. 49 | /// 50 | /// Each structure is detailed individually below. 51 | /// 52 | /// The device MAY offer more than one structure of any type - this makes it 53 | /// possible for the device to expose multiple interfaces to drivers. The order of 54 | /// the capabilities in the capability list specifies the order of preference 55 | /// suggested by the device. A device may specify that this ordering mechanism be 56 | /// overridden by the use of the `id` field. 57 | /// 58 | ///
59 | /// 60 | /// For example, on some hypervisors, notifications using IO accesses are 61 | /// faster than memory accesses. In this case, the device would expose two 62 | /// capabilities with `cfg_type` set to VIRTIO_PCI_CAP_NOTIFY_CFG: 63 | /// the first one addressing an I/O BAR, the second one addressing a memory BAR. 64 | /// In this example, the driver would use the I/O BAR if I/O resources are available, and fall back on 65 | /// memory BAR when I/O resources are unavailable. 66 | /// 67 | ///
68 | pub cfg_type: u8, 69 | 70 | /// Where to find it. 71 | /// 72 | /// values 0x0 to 0x5 specify a Base Address register (BAR) belonging to 73 | /// the function located beginning at 10h in PCI Configuration Space 74 | /// and used to map the structure into Memory or I/O Space. 75 | /// The BAR is permitted to be either 32-bit or 64-bit, it can map Memory Space 76 | /// or I/O Space. 77 | /// 78 | /// Any other value is reserved for future use. 79 | pub bar: u8, 80 | 81 | /// Multiple capabilities of the same type 82 | /// 83 | /// Used by some device types to uniquely identify multiple capabilities 84 | /// of a certain type. If the device type does not specify the meaning of 85 | /// this field, its contents are undefined. 86 | pub id: u8, 87 | 88 | /// Pad to full dword. 89 | pub padding: [u8; 2], 90 | 91 | /// Offset within bar. 92 | /// 93 | /// indicates where the structure begins relative to the base address associated 94 | /// with the BAR. The alignment requirements of `offset` are indicated 95 | /// in each structure-specific section below. 96 | pub offset: le32, 97 | 98 | /// Length of the structure, in bytes. 99 | /// 100 | /// indicates the length of the structure. 101 | /// 102 | /// `length` MAY include padding, or fields unused by the driver, or 103 | /// future extensions. 104 | /// 105 | ///
106 | /// 107 | /// For example, a future device might present a large structure size of several 108 | /// MBytes. 109 | /// As current devices never utilize structures larger than 4KBytes in size, 110 | /// driver MAY limit the mapped structure size to e.g. 111 | /// 4KBytes (thus ignoring parts of structure after the first 112 | /// 4KBytes) to allow forward compatibility with such devices without loss of 113 | /// functionality and without wasting resources. 114 | /// 115 | ///
116 | pub length: le32, 117 | } 118 | 119 | impl Cap { 120 | pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option { 121 | let data = unsafe { access.read(addr.address, addr.offset) }; 122 | let [cap_vndr, _cap_next, cap_len, _cfg_type] = data.to_ne_bytes(); 123 | 124 | if cap_vndr != 0x09 { 125 | return None; 126 | } 127 | 128 | if cap_len < 16 { 129 | return None; 130 | } 131 | 132 | let data = [ 133 | data, 134 | unsafe { access.read(addr.address, addr.offset + 4) }, 135 | unsafe { access.read(addr.address, addr.offset + 8) }, 136 | unsafe { access.read(addr.address, addr.offset + 12) }, 137 | ]; 138 | 139 | let this = unsafe { mem::transmute::<[u32; 4], Self>(data) }; 140 | 141 | Some(this) 142 | } 143 | } 144 | 145 | /// PCI Capability 64 146 | #[doc(alias = "virtio_pci_cap64")] 147 | #[cfg_attr( 148 | feature = "zerocopy", 149 | derive( 150 | zerocopy_derive::KnownLayout, 151 | zerocopy_derive::Immutable, 152 | zerocopy_derive::FromBytes, 153 | ) 154 | )] 155 | #[derive(Clone, Copy, Debug)] 156 | #[repr(C)] 157 | pub struct Cap64 { 158 | pub cap: Cap, 159 | pub offset_hi: le32, 160 | pub length_hi: le32, 161 | } 162 | 163 | /// PCI Notify Capability 164 | #[doc(alias = "virtio_pci_notify_cap")] 165 | #[cfg_attr( 166 | feature = "zerocopy", 167 | derive( 168 | zerocopy_derive::KnownLayout, 169 | zerocopy_derive::Immutable, 170 | zerocopy_derive::FromBytes, 171 | ) 172 | )] 173 | #[derive(Clone, Copy, Debug)] 174 | #[repr(C)] 175 | pub struct NotifyCap { 176 | pub cap: Cap, 177 | 178 | /// Multiplier for queue_notify_off. 179 | pub notify_off_multiplier: le32, 180 | } 181 | 182 | /// PCI Configuration Capability 183 | #[doc(alias = "virtio_pci_cfg_cap")] 184 | #[cfg_attr( 185 | feature = "zerocopy", 186 | derive( 187 | zerocopy_derive::KnownLayout, 188 | zerocopy_derive::Immutable, 189 | zerocopy_derive::FromBytes, 190 | ) 191 | )] 192 | #[derive(Clone, Copy, Debug)] 193 | #[repr(C)] 194 | pub struct CfgCap { 195 | pub cap: Cap, 196 | 197 | /// Data for BAR access. 198 | pub pci_cfg_data: [u8; 4], 199 | } 200 | 201 | /// PCI Capability Data 202 | #[derive(Clone, Copy, Debug)] 203 | pub struct CapData { 204 | /// Identifies the structure. 205 | pub cfg_type: CapCfgType, 206 | 207 | /// Where to find it. 208 | pub bar: u8, 209 | 210 | /// Multiple capabilities of the same type 211 | pub id: u8, 212 | 213 | /// Offset within bar. 214 | pub offset: le64, 215 | 216 | /// Length of the structure, in bytes. 217 | pub length: le64, 218 | 219 | /// Multiplier for queue_notify_off. 220 | pub notify_off_multiplier: Option, 221 | } 222 | 223 | impl CapData { 224 | pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option { 225 | let cap = Cap::read(addr, &access)?; 226 | let cfg_type = CapCfgType::from(cap.cfg_type); 227 | 228 | let (offset, length) = match cfg_type { 229 | CapCfgType::SharedMemory => { 230 | if cap.cap_len < 24 { 231 | return None; 232 | } 233 | 234 | let offset_hi = unsafe { access.read(addr.address, addr.offset + 16) }; 235 | let offset_hi = Le(offset_hi); 236 | let offset = le64::from([cap.offset, offset_hi]); 237 | 238 | let length_hi = unsafe { access.read(addr.address, addr.offset + 20) }; 239 | let length_hi = Le(length_hi); 240 | let length = le64::from([cap.length, length_hi]); 241 | 242 | (offset, length) 243 | } 244 | _ => (le64::from(cap.offset), le64::from(cap.length)), 245 | }; 246 | 247 | let notify_off_multiplier = match cfg_type { 248 | CapCfgType::Notify => { 249 | if cap.cap_len < 20 { 250 | return None; 251 | } 252 | 253 | let notify_off_multiplier = unsafe { access.read(addr.address, addr.offset + 16) }; 254 | let notify_off_multiplier = Le(notify_off_multiplier); 255 | 256 | Some(notify_off_multiplier) 257 | } 258 | _ => None, 259 | }; 260 | 261 | Some(Self { 262 | cfg_type, 263 | bar: cap.bar, 264 | id: cap.id, 265 | offset, 266 | length, 267 | notify_off_multiplier, 268 | }) 269 | } 270 | } 271 | 272 | /// PCI Capability Configuration Type 273 | #[doc(alias = "VIRTIO_PCI_CAP")] 274 | #[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] 275 | #[non_exhaustive] 276 | #[repr(u8)] 277 | pub enum CapCfgType { 278 | /// Common configuration 279 | #[doc(alias = "VIRTIO_PCI_CAP_COMMON_CFG")] 280 | Common = 1, 281 | 282 | /// Notifications 283 | #[doc(alias = "VIRTIO_PCI_CAP_NOTIFY_CFG")] 284 | Notify = 2, 285 | 286 | /// ISR Status 287 | #[doc(alias = "VIRTIO_PCI_CAP_ISR_CFG")] 288 | Isr = 3, 289 | 290 | /// Device specific configuration 291 | #[doc(alias = "VIRTIO_PCI_CAP_DEVICE_CFG")] 292 | Device = 4, 293 | 294 | /// PCI configuration access 295 | #[doc(alias = "VIRTIO_PCI_CAP_PCI_CFG")] 296 | Pci = 5, 297 | 298 | /// Shared memory region 299 | #[doc(alias = "VIRTIO_PCI_CAP_SHARED_MEMORY_CFG")] 300 | SharedMemory = 8, 301 | 302 | /// Vendor-specific data 303 | #[doc(alias = "VIRTIO_PCI_CAP_VENDOR_CFG")] 304 | Vendor = 9, 305 | 306 | /// Unknown device 307 | #[num_enum(catch_all)] 308 | Unknown(u8), 309 | } 310 | 311 | /// Common configuration structure 312 | /// 313 | /// The common configuration structure is found at the bar and offset within the [`VIRTIO_PCI_CAP_COMMON_CFG`] capability. 314 | /// 315 | /// [`VIRTIO_PCI_CAP_COMMON_CFG`]: CapCfgType::Common 316 | /// 317 | /// Use [`CommonCfgVolatileFieldAccess`] and [`CommonCfgVolatileWideFieldAccess`] to work with this struct. 318 | #[doc(alias = "virtio_pci_common_cfg")] 319 | #[cfg_attr( 320 | feature = "zerocopy", 321 | derive( 322 | zerocopy_derive::KnownLayout, 323 | zerocopy_derive::Immutable, 324 | zerocopy_derive::FromBytes, 325 | ) 326 | )] 327 | #[derive(VolatileFieldAccess)] 328 | #[repr(C)] 329 | pub struct CommonCfg { 330 | /// The driver uses this to select which feature bits `device_feature` shows. 331 | /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc. 332 | device_feature_select: le32, 333 | 334 | /// The device uses this to report which feature bits it is 335 | /// offering to the driver: the driver writes to 336 | /// `device_feature_select` to select which feature bits are presented. 337 | #[access(ReadOnly)] 338 | device_feature: le32, 339 | 340 | /// The driver uses this to select which feature bits `driver_feature` shows. 341 | /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc. 342 | driver_feature_select: le32, 343 | 344 | /// The driver writes this to accept feature bits offered by the device. 345 | /// Driver Feature Bits selected by `driver_feature_select`. 346 | driver_feature: le32, 347 | 348 | /// The driver sets the Configuration Vector for MSI-X. 349 | config_msix_vector: le16, 350 | 351 | /// The device specifies the maximum number of virtqueues supported here. 352 | #[access(ReadOnly)] 353 | num_queues: le16, 354 | 355 | /// The driver writes the device status here (see [`DeviceStatus`]). Writing 0 into this 356 | /// field resets the device. 357 | device_status: DeviceStatus, 358 | 359 | /// Configuration atomicity value. The device changes this every time the 360 | /// configuration noticeably changes. 361 | #[access(ReadOnly)] 362 | config_generation: u8, 363 | 364 | /// Queue Select. The driver selects which virtqueue the following 365 | /// fields refer to. 366 | queue_select: le16, 367 | 368 | /// Queue Size. On reset, specifies the maximum queue size supported by 369 | /// the device. This can be modified by the driver to reduce memory requirements. 370 | /// A 0 means the queue is unavailable. 371 | queue_size: le16, 372 | 373 | /// The driver uses this to specify the queue vector for MSI-X. 374 | queue_msix_vector: le16, 375 | 376 | /// The driver uses this to selectively prevent the device from executing requests from this virtqueue. 377 | /// 1 - enabled; 0 - disabled. 378 | queue_enable: le16, 379 | 380 | /// The driver reads this to calculate the offset from start of Notification structure at 381 | /// which this virtqueue is located. 382 | /// 383 | ///
384 | /// 385 | /// This is _not_ an offset in bytes. 386 | /// See _Virtio Transport Options / Virtio Over PCI Bus / PCI Device Layout / Notification capability_ below. 387 | /// 388 | ///
389 | #[access(ReadOnly)] 390 | queue_notify_off: le16, 391 | 392 | /// The driver writes the physical address of Descriptor Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 393 | queue_desc_low: le32, 394 | 395 | /// The driver writes the physical address of Descriptor Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 396 | queue_desc_high: le32, 397 | 398 | /// The driver writes the physical address of Driver Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 399 | queue_driver_low: le32, 400 | 401 | /// The driver writes the physical address of Driver Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 402 | queue_driver_high: le32, 403 | 404 | /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 405 | queue_device_low: le32, 406 | 407 | /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 408 | queue_device_high: le32, 409 | 410 | /// This field exists only if [`VIRTIO_F_NOTIF_CONFIG_DATA`] has been negotiated. 411 | /// The driver will use this value to put it in the 'virtqueue number' field 412 | /// in the available buffer notification structure. 413 | /// See section _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_. 414 | /// 415 | ///
416 | /// 417 | /// This field provides the device with flexibility to determine how virtqueues 418 | /// will be referred to in available buffer notifications. 419 | /// In a trivial case the device can set `queue_notify_data`=vqn. Some devices 420 | /// may benefit from providing another value, for example an internal virtqueue 421 | /// identifier, or an internal offset related to the virtqueue number. 422 | /// 423 | ///
424 | /// 425 | /// [`VIRTIO_F_NOTIF_CONFIG_DATA`]: crate::F::NOTIF_CONFIG_DATA 426 | #[access(ReadOnly)] 427 | queue_notify_data: le16, 428 | 429 | /// The driver uses this to selectively reset the queue. 430 | /// This field exists only if [`VIRTIO_F_RING_RESET`] has been 431 | /// negotiated. (see _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_). 432 | /// 433 | /// [`VIRTIO_F_RING_RESET`]: crate::F::RING_RESET 434 | queue_reset: le16, 435 | } 436 | 437 | impl_wide_field_access! { 438 | /// Common configuration structure 439 | pub trait CommonCfgVolatileWideFieldAccess<'a, A>: CommonCfg { 440 | /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 441 | #[access(ReadWrite)] 442 | queue_desc: queue_desc_low, queue_desc_high; 443 | 444 | /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 445 | #[access(ReadWrite)] 446 | queue_driver: queue_driver_low, queue_driver_high; 447 | 448 | /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. 449 | #[access(ReadWrite)] 450 | queue_device: queue_device_low, queue_device_high; 451 | } 452 | } 453 | 454 | impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, CommonCfg, A> 455 | where 456 | A: RestrictAccess, 457 | A::Restricted: Readable, 458 | { 459 | fn read_config_with(self, f: F) -> T 460 | where 461 | F: FnMut() -> T, 462 | { 463 | let mut f = f; 464 | loop { 465 | let before = self.config_generation().read(); 466 | let read = f(); 467 | let after = self.config_generation().read(); 468 | if after == before { 469 | break read; 470 | } 471 | } 472 | } 473 | } 474 | 475 | virtio_bitflags! { 476 | /// ISR Status 477 | pub struct IsrStatus: u8 { 478 | /// Queue Interrupt 479 | const QUEUE_INTERRUPT = 1 << 0; 480 | 481 | /// Device Configuration Interrupt 482 | const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1; 483 | } 484 | } 485 | -------------------------------------------------------------------------------- /src/pvirtq.rs: -------------------------------------------------------------------------------- 1 | //! Packed virtqueue definitions 2 | 3 | use bitfield_struct::bitfield; 4 | 5 | use crate::{le16, le32, le64, virtq, RingEventFlags}; 6 | 7 | /// Packed Virtqueue Descriptor 8 | #[doc(alias = "pvirtq_desc")] 9 | #[repr(C)] 10 | pub struct Desc { 11 | /// Buffer Address. 12 | pub addr: le64, 13 | 14 | /// Buffer Length. 15 | pub len: le32, 16 | 17 | /// Buffer ID. 18 | pub id: le16, 19 | 20 | /// The flags depending on descriptor type. 21 | pub flags: virtq::DescF, 22 | } 23 | 24 | /// Event Suppression Descriptor 25 | #[doc(alias = "pvirtq_event_suppress")] 26 | #[repr(C)] 27 | pub struct EventSuppress { 28 | /// If desc_event_flags set to RING_EVENT_FLAGS_DESC 29 | pub desc: EventSuppressDesc, 30 | pub flags: EventSuppressFlags, 31 | } 32 | 33 | /// Event Suppression Flags 34 | #[bitfield(u16, repr = le16, from = le16::from_ne, into = le16::to_ne)] 35 | pub struct EventSuppressDesc { 36 | /// Descriptor Ring Change Event Offset 37 | #[bits(15)] 38 | pub desc_event_off: u16, 39 | 40 | /// Descriptor Ring Change Event Wrap Counter 41 | #[bits(1)] 42 | pub desc_event_wrap: u8, 43 | } 44 | 45 | #[bitfield(u16, repr = le16, from = le16::from_ne, into = le16::to_ne)] 46 | pub struct EventSuppressFlags { 47 | /// Descriptor Ring Change Event Flags 48 | #[bits(2)] 49 | pub desc_event_flags: RingEventFlags, 50 | 51 | /// Reserved, set to 0 52 | #[bits(14)] 53 | pub reserved: u16, 54 | } 55 | -------------------------------------------------------------------------------- /src/virtq/alloc.rs: -------------------------------------------------------------------------------- 1 | use ::alloc::alloc::handle_alloc_error; 2 | use allocator_api2::alloc::{AllocError, Allocator, Global}; 3 | use allocator_api2::boxed::Box; 4 | 5 | use super::*; 6 | 7 | impl Avail { 8 | pub fn new(queue_size: u16, has_event_idx: bool) -> Box { 9 | Self::new_in(queue_size, has_event_idx, Global) 10 | } 11 | 12 | pub fn try_new(queue_size: u16, has_event_idx: bool) -> Result, AllocError> { 13 | Self::try_new_in(queue_size, has_event_idx, Global) 14 | } 15 | 16 | pub fn new_in(queue_size: u16, has_event_idx: bool, alloc: A) -> Box { 17 | Self::try_new_in(queue_size, has_event_idx, alloc) 18 | .unwrap_or_else(|_| handle_alloc_error(Self::layout(queue_size, has_event_idx))) 19 | } 20 | 21 | pub fn try_new_in( 22 | queue_size: u16, 23 | has_event_idx: bool, 24 | alloc: A, 25 | ) -> Result, AllocError> { 26 | let layout = Self::layout(queue_size, has_event_idx); 27 | 28 | let mem = alloc.allocate_zeroed(layout)?; 29 | let mem = NonNull::slice_from_raw_parts(mem.cast(), layout.size()); 30 | let raw = Self::from_ptr(mem).unwrap(); 31 | let boxed = unsafe { Box::from_raw_in(raw.as_ptr(), alloc) }; 32 | 33 | debug_assert_eq!(Layout::for_value(&*boxed), layout); 34 | debug_assert_eq!(boxed.ring(has_event_idx).len(), queue_size.into()); 35 | debug_assert_eq!(boxed.used_event(has_event_idx).is_some(), has_event_idx); 36 | 37 | Ok(boxed) 38 | } 39 | } 40 | 41 | impl Used { 42 | pub fn new(queue_size: u16, has_event_idx: bool) -> Box { 43 | Self::new_in(queue_size, has_event_idx, Global) 44 | } 45 | 46 | pub fn try_new(queue_size: u16, has_event_idx: bool) -> Result, AllocError> { 47 | Self::try_new_in(queue_size, has_event_idx, Global) 48 | } 49 | 50 | pub fn new_in(queue_size: u16, has_event_idx: bool, alloc: A) -> Box { 51 | Self::try_new_in(queue_size, has_event_idx, alloc) 52 | .unwrap_or_else(|_| handle_alloc_error(Self::layout(queue_size, has_event_idx))) 53 | } 54 | 55 | pub fn try_new_in( 56 | queue_size: u16, 57 | has_event_idx: bool, 58 | alloc: A, 59 | ) -> Result, AllocError> { 60 | let layout = Self::layout(queue_size, has_event_idx); 61 | 62 | let mem = alloc.allocate_zeroed(layout)?; 63 | let mem = NonNull::slice_from_raw_parts(mem.cast(), layout.size()); 64 | let raw = Self::from_ptr(mem, has_event_idx).unwrap(); 65 | let boxed = unsafe { Box::from_raw_in(raw.as_ptr(), alloc) }; 66 | 67 | debug_assert_eq!(Layout::for_value(&*boxed), layout); 68 | debug_assert_eq!(boxed.ring().len(), queue_size.into()); 69 | debug_assert_eq!(boxed.avail_event().is_some(), has_event_idx); 70 | 71 | Ok(boxed) 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn avail_layout() { 81 | for queue_size in [255, 256, 257] { 82 | for has_event_idx in [false, true] { 83 | Avail::new(queue_size, has_event_idx); 84 | } 85 | } 86 | } 87 | 88 | #[test] 89 | fn used_layout() { 90 | for queue_size in [255, 256, 257] { 91 | for has_event_idx in [false, true] { 92 | Used::new(queue_size, has_event_idx); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/virtq/mod.rs: -------------------------------------------------------------------------------- 1 | //! Virtqueue definitions 2 | 3 | #[cfg(feature = "alloc")] 4 | mod alloc; 5 | 6 | use core::alloc::Layout; 7 | use core::ptr::{addr_of_mut, NonNull}; 8 | use core::{mem, ptr}; 9 | 10 | use crate::{le16, le32, le64}; 11 | 12 | /// Split Virtqueue Descriptor 13 | #[doc(alias = "virtq_desc")] 14 | #[derive(Clone, Copy, Debug)] 15 | #[repr(C)] 16 | pub struct Desc { 17 | /// Address (guest-physical). 18 | pub addr: le64, 19 | 20 | /// Length. 21 | pub len: le32, 22 | 23 | /// The flags as indicated in [`DescF`]. 24 | pub flags: DescF, 25 | 26 | /// Next field if flags & NEXT 27 | pub next: le16, 28 | } 29 | 30 | endian_bitflags! { 31 | /// Virtqueue descriptor flags 32 | #[doc(alias = "VIRTQ_DESC_F")] 33 | pub struct DescF: le16 { 34 | /// This marks a buffer as continuing via the next field. 35 | #[doc(alias = "VIRTQ_DESC_F_NEXT")] 36 | const NEXT = 1; 37 | 38 | /// This marks a buffer as device write-only (otherwise device read-only). 39 | #[doc(alias = "VIRTQ_DESC_F_WRITE")] 40 | const WRITE = 2; 41 | 42 | /// This means the buffer contains a list of buffer descriptors. 43 | #[doc(alias = "VIRTQ_DESC_F_INDIRECT")] 44 | const INDIRECT = 4; 45 | 46 | #[doc(alias = "VIRTQ_DESC_F_AVAIL")] 47 | const AVAIL = 1 << 7; 48 | 49 | #[doc(alias = "VIRTQ_DESC_F_USED")] 50 | const USED = 1 << 15; 51 | } 52 | } 53 | 54 | /// The Virtqueue Available Ring 55 | #[doc(alias = "virtq_avail")] 56 | #[derive(Debug)] 57 | #[repr(C)] 58 | pub struct Avail { 59 | pub flags: AvailF, 60 | pub idx: le16, 61 | ring_and_used_event: [le16], 62 | } 63 | 64 | impl Avail { 65 | pub fn layout(queue_size: u16, has_event_idx: bool) -> Layout { 66 | Layout::array::(2 + usize::from(queue_size) + usize::from(has_event_idx)).unwrap() 67 | } 68 | 69 | pub fn from_ptr(ptr: NonNull<[u8]>) -> Option> { 70 | let len = ptr.as_ptr().len(); 71 | // FIXME: use ptr::as_mut_ptr once stable 72 | // https://github.com/rust-lang/rust/issues/74265 73 | let ptr = ptr.as_ptr() as *mut u8; 74 | 75 | if !ptr.cast::().is_aligned() { 76 | return None; 77 | } 78 | 79 | if len % mem::size_of::() != 0 { 80 | return None; 81 | } 82 | 83 | let len = len / mem::size_of::() - 2; 84 | let ptr = ptr::slice_from_raw_parts_mut(ptr, len) as *mut Self; 85 | Some(NonNull::new(ptr).unwrap()) 86 | } 87 | 88 | pub fn ring_ptr(this: NonNull, has_event_idx: bool) -> NonNull<[le16]> { 89 | let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_used_event) }; 90 | let len = if cfg!(debug_assertions) { 91 | ptr.len() 92 | .checked_sub(usize::from(has_event_idx)) 93 | .expect("`has_event_idx` cannot be true if it was not true at creation") 94 | } else { 95 | ptr.len().saturating_sub(usize::from(has_event_idx)) 96 | }; 97 | let ptr = NonNull::new(ptr).unwrap().cast::(); 98 | NonNull::slice_from_raw_parts(ptr, len) 99 | } 100 | 101 | pub fn ring(&self, has_event_idx: bool) -> &[le16] { 102 | let ptr = Self::ring_ptr(NonNull::from(self), has_event_idx); 103 | unsafe { ptr.as_ref() } 104 | } 105 | 106 | pub fn ring_mut(&mut self, has_event_idx: bool) -> &mut [le16] { 107 | let mut ptr = Self::ring_ptr(NonNull::from(self), has_event_idx); 108 | unsafe { ptr.as_mut() } 109 | } 110 | 111 | pub fn used_event_ptr(this: NonNull, has_event_idx: bool) -> Option> { 112 | if !has_event_idx { 113 | return None; 114 | } 115 | 116 | let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_used_event) }; 117 | let len = ptr.len(); 118 | 119 | if len == 0 { 120 | return None; 121 | } 122 | 123 | let ptr = NonNull::new(ptr).unwrap().cast::(); 124 | let ptr = unsafe { ptr.add(len - 1) }; 125 | Some(ptr) 126 | } 127 | 128 | pub fn used_event(&self, has_event_idx: bool) -> Option<&le16> { 129 | Self::used_event_ptr(NonNull::from(self), has_event_idx).map(|ptr| unsafe { ptr.as_ref() }) 130 | } 131 | 132 | pub fn used_event_mut(&mut self, has_event_idx: bool) -> Option<&mut le16> { 133 | Self::used_event_ptr(NonNull::from(self), has_event_idx) 134 | .map(|mut ptr| unsafe { ptr.as_mut() }) 135 | } 136 | } 137 | 138 | endian_bitflags! { 139 | /// Virtqueue available ring flags 140 | #[doc(alias = "VIRTQ_AVAIL_F")] 141 | pub struct AvailF: le16 { 142 | /// The driver uses this in avail->flags to advise the device: don’t 143 | /// interrupt me when you consume a buffer. It’s unreliable, so it’s 144 | /// simply an optimization. 145 | #[doc(alias = "VIRTQ_AVAIL_F_NO_INTERRUPT")] 146 | const NO_INTERRUPT = 1; 147 | } 148 | } 149 | 150 | /// The Virtqueue Used Ring 151 | #[doc(alias = "virtq_used")] 152 | #[derive(Debug)] 153 | #[repr(C)] 154 | #[repr(align(4))] // mem::align_of:: 155 | pub struct Used { 156 | pub flags: UsedF, 157 | pub idx: le16, 158 | ring_and_avail_event: [le16], 159 | } 160 | 161 | impl Used { 162 | pub fn layout(queue_size: u16, has_event_idx: bool) -> Layout { 163 | let event_idx_layout = if has_event_idx { 164 | Layout::new::() 165 | } else { 166 | Layout::new::<()>() 167 | }; 168 | 169 | Layout::array::(2) 170 | .unwrap() 171 | .extend(Layout::array::(queue_size.into()).unwrap()) 172 | .unwrap() 173 | .0 174 | .extend(event_idx_layout) 175 | .unwrap() 176 | .0 177 | .pad_to_align() 178 | } 179 | 180 | pub fn from_ptr(ptr: NonNull<[u8]>, has_event_idx: bool) -> Option> { 181 | let len = ptr.len(); 182 | let ptr = ptr.cast::().as_ptr(); 183 | 184 | if !ptr.cast::().is_aligned() { 185 | return None; 186 | } 187 | 188 | if len % mem::size_of::() != usize::from(!has_event_idx) * mem::size_of::() 189 | { 190 | return None; 191 | } 192 | 193 | let len = len / mem::size_of::() - 2 - usize::from(has_event_idx); 194 | let ptr = ptr::slice_from_raw_parts(ptr, len) as *mut Used; 195 | Some(NonNull::new(ptr).unwrap()) 196 | } 197 | 198 | pub fn ring_ptr(this: NonNull) -> NonNull<[UsedElem]> { 199 | let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_avail_event) }; 200 | let len = ptr.len() * mem::size_of::() / mem::size_of::(); 201 | let ptr = NonNull::new(ptr).unwrap().cast::(); 202 | NonNull::slice_from_raw_parts(ptr, len) 203 | } 204 | 205 | pub fn ring(&self) -> &[UsedElem] { 206 | let ptr = Self::ring_ptr(NonNull::from(self)); 207 | unsafe { ptr.as_ref() } 208 | } 209 | 210 | pub fn ring_mut(&mut self) -> &mut [UsedElem] { 211 | let mut ptr = Self::ring_ptr(NonNull::from(self)); 212 | unsafe { ptr.as_mut() } 213 | } 214 | 215 | pub fn avail_event_ptr(this: NonNull) -> Option> { 216 | let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_avail_event) }; 217 | 218 | if ptr.len() * mem::size_of::() % mem::size_of::() != mem::size_of::() 219 | { 220 | return None; 221 | } 222 | 223 | let start = ptr as *mut le16; 224 | let ptr = unsafe { start.add(ptr.len() - 1) }; 225 | Some(NonNull::new(ptr).unwrap()) 226 | } 227 | 228 | pub fn avail_event(&self) -> Option<&le16> { 229 | Self::avail_event_ptr(NonNull::from(self)).map(|ptr| unsafe { ptr.as_ref() }) 230 | } 231 | 232 | pub fn avail_event_mut(&mut self) -> Option<&mut le16> { 233 | Self::avail_event_ptr(NonNull::from(self)).map(|mut ptr| unsafe { ptr.as_mut() }) 234 | } 235 | } 236 | 237 | endian_bitflags! { 238 | /// Virtqueue used ring flags 239 | #[doc(alias = "VIRTQ_USED_F")] 240 | pub struct UsedF: le16 { 241 | /// The device uses this in used->flags to advise the driver: don’t kick me 242 | /// when you add a buffer. It’s unreliable, so it’s simply an 243 | /// optimization. 244 | #[doc(alias = "VIRTQ_USED_F_NO_NOTIFY")] 245 | const NO_NOTIFY = 1; 246 | } 247 | } 248 | 249 | /// Used Ring Entry 250 | #[doc(alias = "virtq_used_elem")] 251 | #[derive(Clone, Copy, Debug)] 252 | #[repr(C)] 253 | pub struct UsedElem { 254 | /// Index of start of used descriptor chain. 255 | /// 256 | /// [`le32`] is used here for ids for padding reasons. 257 | pub id: le32, 258 | 259 | /// The number of bytes written into the device writable portion of 260 | /// the buffer described by the descriptor chain. 261 | pub len: le32, 262 | } 263 | -------------------------------------------------------------------------------- /src/volatile.rs: -------------------------------------------------------------------------------- 1 | //! Volatile Pointer Types. 2 | 3 | use core::marker::PhantomData; 4 | 5 | use volatile::access::{Readable, Writable}; 6 | use volatile::VolatilePtr; 7 | 8 | use crate::{be32, be64, le16, le32, le64, DeviceStatus, Id}; 9 | 10 | /// A wide volatile pointer for 64-bit fields. 11 | /// 12 | /// In virtio, 64-bit fields are to be treated as two 32-bit fields, with low 32 bit part followed by the high 32 bit part. 13 | /// This type mimics [`VolatilePtr`], and allows easy access to 64-bit fields. 14 | pub struct WideVolatilePtr<'a, T, A> 15 | where 16 | T: ?Sized, 17 | { 18 | low: VolatilePtr<'a, T, A>, 19 | high: VolatilePtr<'a, T, A>, 20 | } 21 | 22 | impl<'a, T, A> Copy for WideVolatilePtr<'a, T, A> where T: ?Sized {} 23 | 24 | impl<'a, T, A> Clone for WideVolatilePtr<'a, T, A> 25 | where 26 | T: ?Sized, 27 | { 28 | fn clone(&self) -> Self { 29 | *self 30 | } 31 | } 32 | 33 | impl<'a, T, A> WideVolatilePtr<'a, T, A> { 34 | /// Creates a new wide volatile pointer from pointers to the low and to the high part. 35 | pub fn from_low_high(low: VolatilePtr<'a, T, A>, high: VolatilePtr<'a, T, A>) -> Self { 36 | Self { low, high } 37 | } 38 | } 39 | 40 | impl<'a, A> WideVolatilePtr<'a, le32, A> { 41 | /// Performs a volatile read of the contained value. 42 | /// 43 | /// See [`VolatilePtr::read`]. 44 | pub fn read(self) -> le64 45 | where 46 | A: Readable, 47 | { 48 | let low = self.low.read(); 49 | let high = self.high.read(); 50 | le64::from([low, high]) 51 | } 52 | 53 | /// Performs a volatile write, setting the contained value to the given `value`. 54 | /// 55 | /// See [`VolatilePtr::write`]. 56 | pub fn write(self, value: le64) 57 | where 58 | A: Writable, 59 | { 60 | let [low, high] = value.into(); 61 | self.low.write(low); 62 | self.high.write(high); 63 | } 64 | 65 | /// Updates the contained value using the given closure and volatile instructions. 66 | /// 67 | /// See [`VolatilePtr::update`]. 68 | pub fn update(self, f: impl FnOnce(le64) -> le64) 69 | where 70 | A: Readable + Writable, 71 | { 72 | let new = f(self.read()); 73 | self.write(new); 74 | } 75 | } 76 | 77 | impl<'a, A> WideVolatilePtr<'a, be32, A> { 78 | /// Performs a volatile read of the contained value. 79 | /// 80 | /// See [`VolatilePtr::read`]. 81 | pub fn read(self) -> be64 82 | where 83 | A: Readable, 84 | { 85 | let low = self.low.read(); 86 | let high = self.high.read(); 87 | be64::from([low, high]) 88 | } 89 | 90 | /// Performs a volatile write, setting the contained value to the given `value`. 91 | /// 92 | /// See [`VolatilePtr::write`]. 93 | pub fn write(self, value: be64) 94 | where 95 | A: Writable, 96 | { 97 | let [low, high] = value.into(); 98 | self.low.write(low); 99 | self.high.write(high); 100 | } 101 | 102 | /// Updates the contained value using the given closure and volatile instructions. 103 | /// 104 | /// See [`VolatilePtr::update`]. 105 | pub fn update(self, f: impl FnOnce(be64) -> be64) 106 | where 107 | A: Readable + Writable, 108 | { 109 | let new = f(self.read()); 110 | self.write(new); 111 | } 112 | } 113 | 114 | #[cfg(any(feature = "mmio", feature = "pci"))] 115 | macro_rules! impl_wide_field_access { 116 | ( 117 | $(#[$outer:meta])* 118 | $vis:vis trait $Trait:ident<'a, A>: $T:ty { 119 | $( 120 | $(#[doc = $doc:literal])* 121 | $(#[doc(alias = $alias:literal)])? 122 | #[access($Access:ty)] 123 | $field:ident: $field_low:ident, $field_high:ident; 124 | )* 125 | } 126 | ) => { 127 | $(#[$outer])* 128 | $vis trait $Trait<'a, A> { 129 | $( 130 | $(#[doc = $doc])* 131 | $(#[doc(alias = $alias)])? 132 | fn $field(self) -> WideVolatilePtr<'a, le32, A::Restricted> 133 | where 134 | A: RestrictAccess<$Access>; 135 | )* 136 | } 137 | 138 | impl<'a, A> $Trait<'a, A> for VolatilePtr<'a, $T, A> { 139 | $( 140 | fn $field(self) -> WideVolatilePtr<'a, le32, A::Restricted> 141 | where 142 | A: RestrictAccess<$Access>, 143 | { 144 | WideVolatilePtr::from_low_high(self.$field_low(), self.$field_high()) 145 | } 146 | )* 147 | } 148 | }; 149 | } 150 | 151 | /// An overaligned volatile pointer for fields that require wider access operations. 152 | /// 153 | /// In virtio, some fields require wider access operations than their type indicate, such as for [`mmio::DeviceRegisters`]. 154 | /// 155 | /// [`mmio::DeviceRegisters`]: crate::mmio::DeviceRegisters 156 | pub struct OveralignedVolatilePtr<'a, T, F, A> 157 | where 158 | T: ?Sized, 159 | F: ?Sized, 160 | { 161 | ptr: VolatilePtr<'a, F, A>, 162 | ty: PhantomData>, 163 | } 164 | 165 | impl<'a, T, F, A> Copy for OveralignedVolatilePtr<'a, T, F, A> 166 | where 167 | T: ?Sized, 168 | F: ?Sized, 169 | { 170 | } 171 | 172 | impl<'a, T, F, A> Clone for OveralignedVolatilePtr<'a, T, F, A> 173 | where 174 | T: ?Sized, 175 | F: ?Sized, 176 | { 177 | fn clone(&self) -> Self { 178 | *self 179 | } 180 | } 181 | 182 | impl<'a, T, F, A> OveralignedVolatilePtr<'a, T, F, A> 183 | where 184 | T: OveralignedField, 185 | F: Copy, 186 | { 187 | /// Creates a new overaligned volatile pointer. 188 | pub fn new(ptr: VolatilePtr<'a, F, A>) -> Self { 189 | Self { 190 | ptr, 191 | ty: PhantomData, 192 | } 193 | } 194 | 195 | /// Performs a volatile read of the contained value. 196 | /// 197 | /// See [`VolatilePtr::read`]. 198 | pub fn read(self) -> T 199 | where 200 | A: Readable, 201 | { 202 | T::from_field(self.ptr.read()) 203 | } 204 | 205 | /// Performs a volatile write, setting the contained value to the given `value`. 206 | /// 207 | /// See [`VolatilePtr::write`]. 208 | pub fn write(self, value: T) 209 | where 210 | A: Writable, 211 | { 212 | self.ptr.write(value.into_field()) 213 | } 214 | 215 | /// Updates the contained value using the given closure and volatile instructions. 216 | /// 217 | /// See [`VolatilePtr::update`]. 218 | pub fn update(self, f: impl FnOnce(T) -> T) 219 | where 220 | A: Readable + Writable, 221 | { 222 | let new = f(self.read()); 223 | self.write(new); 224 | } 225 | } 226 | 227 | /// A trait for fields that can be accessed via [`OveralignedVolatilePtr`]. 228 | pub trait OveralignedField: private::Sealed { 229 | /// Converts to this type from the overaligned field. 230 | fn from_field(field: F) -> Self; 231 | 232 | /// Converts this type into the overaligned field. 233 | fn into_field(self) -> F; 234 | } 235 | 236 | impl OveralignedField for le16 { 237 | fn from_field(field: le32) -> Self { 238 | field.try_into().unwrap() 239 | } 240 | 241 | fn into_field(self) -> le32 { 242 | self.into() 243 | } 244 | } 245 | 246 | impl OveralignedField for bool { 247 | fn from_field(field: le32) -> Self { 248 | field.to_ne() == 1 249 | } 250 | 251 | fn into_field(self) -> le32 { 252 | le32::from_ne(self as u32) 253 | } 254 | } 255 | 256 | impl OveralignedField for u8 { 257 | fn from_field(field: le32) -> Self { 258 | field.to_ne().try_into().unwrap() 259 | } 260 | 261 | fn into_field(self) -> le32 { 262 | le32::from_ne(self.into()) 263 | } 264 | } 265 | 266 | impl OveralignedField for Id { 267 | fn from_field(field: le32) -> Self { 268 | Self::from(u8::from_field(field)) 269 | } 270 | 271 | fn into_field(self) -> le32 { 272 | u8::from(self).into_field() 273 | } 274 | } 275 | 276 | impl OveralignedField for DeviceStatus { 277 | fn from_field(field: le32) -> Self { 278 | Self::from_bits_retain(u8::from_field(field)) 279 | } 280 | 281 | fn into_field(self) -> le32 { 282 | self.bits().into_field() 283 | } 284 | } 285 | 286 | #[cfg(feature = "mmio")] 287 | impl OveralignedField for crate::mmio::InterruptStatus { 288 | fn from_field(field: le32) -> Self { 289 | Self::from_bits_retain(u8::from_field(field)) 290 | } 291 | 292 | fn into_field(self) -> le32 { 293 | self.bits().into_field() 294 | } 295 | } 296 | 297 | mod private { 298 | use crate::{le16, le32, DeviceStatus, Id}; 299 | 300 | pub trait Sealed {} 301 | 302 | impl Sealed for bool {} 303 | impl Sealed for u8 {} 304 | impl Sealed for le16 {} 305 | impl Sealed for Id {} 306 | impl Sealed for DeviceStatus {} 307 | #[cfg(feature = "mmio")] 308 | impl Sealed for crate::mmio::InterruptStatus {} 309 | } 310 | -------------------------------------------------------------------------------- /src/vsock.rs: -------------------------------------------------------------------------------- 1 | //! Socket Device 2 | 3 | use endian_num::{le16, le32}; 4 | use num_enum::{IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive}; 5 | use volatile_macro::VolatileFieldAccess; 6 | 7 | pub use super::features::vsock::F; 8 | use crate::le64; 9 | 10 | /// Socket Device Configuration Layout 11 | /// 12 | /// Use [`ConfigVolatileFieldAccess`] to work with this struct. 13 | #[doc(alias = "virtio_vsock_config")] 14 | #[cfg_attr( 15 | feature = "zerocopy", 16 | derive( 17 | zerocopy_derive::KnownLayout, 18 | zerocopy_derive::Immutable, 19 | zerocopy_derive::FromBytes, 20 | ) 21 | )] 22 | #[derive(VolatileFieldAccess)] 23 | #[repr(C)] 24 | pub struct Config { 25 | guest_cid: le64, 26 | } 27 | 28 | /// Socket Device Header 29 | #[doc(alias = "virtio_vsock_hdr")] 30 | #[cfg_attr( 31 | feature = "zerocopy", 32 | derive( 33 | zerocopy_derive::KnownLayout, 34 | zerocopy_derive::Immutable, 35 | zerocopy_derive::FromBytes, 36 | zerocopy_derive::IntoBytes, 37 | ) 38 | )] 39 | #[derive(Default, Clone, Copy, Debug)] 40 | #[repr(C, packed)] 41 | pub struct Hdr { 42 | pub src_cid: le64, 43 | pub dst_cid: le64, 44 | pub src_port: le32, 45 | pub dst_port: le32, 46 | pub len: le32, 47 | pub type_: le16, 48 | pub op: le16, 49 | pub flags: le32, 50 | pub buf_alloc: le32, 51 | pub fwd_cnt: le32, 52 | } 53 | 54 | #[doc(alias = "VIRTIO_VSOCK_OP")] 55 | #[derive( 56 | IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive, PartialEq, Eq, Clone, Copy, Debug, 57 | )] 58 | #[non_exhaustive] 59 | #[repr(u16)] 60 | pub enum Op { 61 | #[doc(alias = "VIRTIO_VSOCK_OP_INVALID")] 62 | Invalid = 0, 63 | 64 | #[doc(alias = "VIRTIO_VSOCK_OP_REQUEST")] 65 | Request = 1, 66 | 67 | #[doc(alias = "VIRTIO_VSOCK_OP_RESPONSE")] 68 | Response = 2, 69 | 70 | #[doc(alias = "VIRTIO_VSOCK_OP_RST")] 71 | Rst = 3, 72 | 73 | #[doc(alias = "VIRTIO_VSOCK_OP_SHUTDOWN")] 74 | Shutdown = 4, 75 | 76 | #[doc(alias = "VIRTIO_VSOCK_OP_RW")] 77 | Rw = 5, 78 | 79 | #[doc(alias = "VIRTIO_VSOCK_OP_CREDIT_UPDATE")] 80 | CreditUpdate = 6, 81 | 82 | #[doc(alias = "VIRTIO_VSOCK_OP_CREDIT_REQUEST")] 83 | CreditRequest = 7, 84 | } 85 | 86 | #[doc(alias = "VIRTIO_VSOCK_TYPE")] 87 | #[derive( 88 | IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive, PartialEq, Eq, Clone, Copy, Debug, 89 | )] 90 | #[non_exhaustive] 91 | #[repr(u16)] 92 | pub enum Type { 93 | #[doc(alias = "VIRTIO_VSOCK_TYPE_STREAM")] 94 | Stream = 1, 95 | 96 | #[doc(alias = "VIRTIO_VSOCK_TYPE_SEQPACKET")] 97 | Seqpacket = 2, 98 | } 99 | 100 | endian_bitflags! { 101 | /// Socket Device Shutdown Flags 102 | #[doc(alias = "VIRTIO_VSOCK_SHUTDOWN_F")] 103 | pub struct ShutdownF: le32 { 104 | #[doc(alias = "VIRTIO_VSOCK_SHUTDOWN_F_RECEIVE")] 105 | const RECEIVE = 1 << 0; 106 | 107 | #[doc(alias = "VIRTIO_VSOCK_SHUTDOWN_F_SEND")] 108 | const SEND = 1 << 1; 109 | } 110 | } 111 | 112 | endian_bitflags! { 113 | /// Socket Device Sequence Flags 114 | #[doc(alias = "VIRTIO_VSOCK_SEQ")] 115 | pub struct Seq: le32 { 116 | #[doc(alias = "VIRTIO_VSOCK_SEQ_EOM")] 117 | const EOM = 1 << 0; 118 | 119 | #[doc(alias = "VIRTIO_VSOCK_SEQ_EOR")] 120 | const EOR = 1 << 1; 121 | } 122 | } 123 | 124 | #[doc(alias = "VIRTIO_VSOCK_EVENT")] 125 | #[derive( 126 | IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive, PartialEq, Eq, Clone, Copy, Debug, 127 | )] 128 | #[non_exhaustive] 129 | #[repr(u32)] 130 | pub enum EventId { 131 | #[doc(alias = "VIRTIO_VSOCK_EVENT_TRANSPORT_RESET")] 132 | TransportReset = 0, 133 | } 134 | 135 | /// Socket Device Event 136 | #[doc(alias = "virtio_vsock_event")] 137 | #[cfg_attr( 138 | feature = "zerocopy", 139 | derive( 140 | zerocopy_derive::KnownLayout, 141 | zerocopy_derive::Immutable, 142 | zerocopy_derive::FromBytes, 143 | zerocopy_derive::IntoBytes, 144 | ) 145 | )] 146 | #[derive(Default, Clone, Copy, Debug)] 147 | #[repr(C)] 148 | pub struct Event { 149 | id: le32, 150 | } 151 | --------------------------------------------------------------------------------