├── .buildkite └── custom-tests.json ├── .cargo └── config ├── .github └── dependabot.yml ├── .gitignore ├── .gitmodules ├── CODEOWNERS ├── Cargo.toml ├── README.md ├── coverage_config_aarch64.json ├── coverage_config_x86_64.json └── crates ├── vfio-bindings ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-BSD ├── README.md └── src │ ├── bindings_v5_0_0 │ ├── mod.rs │ └── vfio.rs │ ├── fam_wrappers.rs │ └── lib.rs └── vfio-ioctls ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-BSD ├── README.md └── src ├── fam.rs ├── lib.rs ├── vfio_device.rs └── vfio_ioctls.rs /.buildkite/custom-tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests": [ 3 | { 4 | "test_name": "build-mshv", 5 | "command": "cargo build --release --no-default-features --features mshv", 6 | "platform": ["x86_64"] 7 | }, 8 | { 9 | "test_name": "clippy-mshv", 10 | "command": "cargo clippy --workspace --bins --examples --benches --no-default-features --features mshv --all-targets -- -D warnings", 11 | "platform": ["x86_64"] 12 | }, 13 | { 14 | "test_name": "build-nohv", 15 | "command": "cargo build --release --no-default-features", 16 | "platform": ["x86_64"] 17 | }, 18 | { 19 | "test_name": "clippy-nohv", 20 | "command": "cargo clippy --workspace --bins --examples --benches --no-default-features --all-targets -- -D warnings", 21 | "platform": ["x86_64"] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-musl] 2 | rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc"] 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gitsubmodule 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | target 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rust-vmm-ci"] 2 | path = rust-vmm-ci 3 | url = https://github.com/rust-vmm/rust-vmm-ci.git 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Add the list of code owners here (using their GitHub username) 2 | * @jiangliu @liuw @sameo @sboeuf 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/vfio-bindings", 4 | "crates/vfio-ioctls", 5 | ] 6 | resolver = "2" 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Safe wrappers for VFIO 2 | 3 | ## Design 4 | 5 | This repository provides safe wrappers over the 6 | [VFIO driver framework](https://www.kernel.org/doc/Documentation/vfio.txt). 7 | 8 | Many modern systems now provide DMA and interrupt remapping facilities to help ensure I/O devices behave within the boundaries they’ve been allotted. This includes x86 hardware with AMD-Vi and Intel VT-d, POWER systems with Partitionable Endpoints (PEs) and embedded PowerPC systems such as Freescale PAMU. The VFIO driver is an IOMMU/device agnostic framework for exposing direct device access to userspace, in a secure, IOMMU protected environment. In other words, the VFIO framework allows safe, non-privileged, userspace drivers. 9 | 10 | Why do we want that? Virtual machines often make use of direct device access (“device assignment”) when configured for the highest possible I/O performance. From a device and host perspective, this simply turns the VM into a userspace driver, with the benefits of significantly reduced latency, higher bandwidth, and direct use of bare-metal device drivers. 11 | 12 | Devices are the main target of any I/O driver. Devices typically create a programming interface made up of I/O accesses, interrupts, and DMA. Without going into the details of each of these, DMA is by far the most critical aspect for maintaining a secure environment as allowing a device read-write access to system memory imposes the greatest risk to the overall system integrity. 13 | 14 | To help mitigate this risk, many modern IOMMUs now incorporate isolation properties into what was, in many cases, an interface only meant for translation (ie. solving the addressing problems of devices with limited address spaces). With this, devices can now be isolated from each other and from arbitrary memory access, thus allowing things like secure direct assignment of devices into virtual machines. 15 | 16 | While for the most part an IOMMU may have device level granularity, any system is susceptible to expose a reduced granularity. The IOMMU API therefore supports a notion of IOMMU groups. A group is a set of devices which is isolated from all other devices in the system. Groups are therefore the unit of ownership used by VFIO. 17 | 18 | While the group is the minimum granularity that must be used to ensure secure user access, it’s not necessarily the preferred granularity. In IOMMUs which make use of page tables, it may be possible to share a set of page tables between different groups, reducing the overhead both to the platform (reduced TLB thrashing, reduced duplicate page tables), and to the user (programming only a single set of translations). For this reason, VFIO makes use of a container class, which may hold one or more groups. A container is created by simply opening the /dev/vfio/vfio character device. 19 | 20 | ## Usage 21 | This repository provides two crates to use the VFIO framework, please refer to crate documentations for detail information. 22 | - [vfio-bindings](https://github.com/rust-vmm/vfio-ioctls/tree/ioctls/crates/vfio-bindings): a rust FFI bindings to VFIO generated using [bindgen](https://crates.io/crates/bindgen). 23 | - [vfio-ioctls](https://github.com/rust-vmm/vfio-ioctls/tree/ioctls/crates/vfio-ioctls): a group of safe wrappers over the [VFIO APIs](https://github.com/torvalds/linux/blob/master/include/uapi/linux/vfio.h). 24 | 25 | ## License 26 | 27 | This code is licensed under Apache-2.0 or BSD-3-Clause. 28 | -------------------------------------------------------------------------------- /coverage_config_aarch64.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage_score": 0, 3 | "exclude_path": "", 4 | "crate_features": "" 5 | } 6 | -------------------------------------------------------------------------------- /coverage_config_x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage_score": 77.4, 3 | "exclude_path": "", 4 | "crate_features": "" 5 | } 6 | -------------------------------------------------------------------------------- /crates/vfio-bindings/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [v0.3.0] 2 | 3 | ## Added 4 | 5 | - Update vmm-sys-util version to ">=0.8.0" 6 | 7 | # [v0.2.0] 8 | 9 | ## Added 10 | 11 | - Add FAM wrappers for vfio\_irq\_set 12 | - Update vmm-sys-util version to ">=0.2.0" 13 | 14 | # [v0.1.0] 15 | 16 | This is the first `vfio-bindings` crate release. 17 | 18 | This crate provides Rust FFI bindings to the 19 | [Virtual Function I/O (VFIO)](https://www.kernel.org/doc/Documentation/vfio.txt) 20 | Linux kernel API. With this first release, the bindings are for the Linux kernel 21 | version 5.0. 22 | 23 | The bindings are generated using [bindgen](https://crates.io/crates/bindgen). 24 | -------------------------------------------------------------------------------- /crates/vfio-bindings/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to vfio-bindings 2 | 3 | ## Dependencies 4 | 5 | ### Bindgen 6 | The bindings are currently generated using 7 | [bindgen](https://crates.io/crates/bindgen): 8 | 9 | ```bash 10 | cargo install bindgen 11 | ``` 12 | 13 | ### Linux Kernel 14 | Generating bindings depends on the Linux kernel, so you need to have the 15 | repository on your machine: 16 | 17 | ```bash 18 | git clone https://github.com/torvalds/linux.git 19 | ``` 20 | 21 | ## Example for adding a new version 22 | 23 | For this example we assume that you have both linux and vfio-bindings 24 | repositories in your root and we will use linux version v5.2 as example. 25 | 26 | ```bash 27 | # Step 1: Crate a new module using a name with format "bindings_vVERSION" in 28 | # src/ 29 | cd vfio-bindings 30 | mkdir src/bindings_v5_2_0 31 | cd ~ 32 | 33 | # Step 2: Copy the "mod.rs" file from the directory of an already existing 34 | # version module to the one we've just created. 35 | cd vfio-bindings/src 36 | cp bindings_v5_0_0/mod.rs bindings_v5_2_0/mod.rs 37 | 38 | # linux is the repository that you cloned at the previous step. 39 | cd linux 40 | 41 | # Step 3: Checkout the version you want to generate the bindings for. 42 | git checkout v5.2 43 | 44 | # Step 4: Generate the bindings from the kernel headers. 45 | make headers_install INSTALL_HDR_PATH=v5_2_headers 46 | cd v5_2_headers 47 | bindgen include/linux/vfio.h -o vfio.rs \ 48 | --with-derive-default \ 49 | --with-derive-partialeq \ 50 | -- -Iinclude 51 | 52 | cd ~ 53 | 54 | # Step 5: Copy the generated files to the new version module. 55 | cp linux/v5_2_headers/vfio.rs vfio-bindings/src/bindings_v5_2_0 56 | ``` 57 | 58 | Once this is done, you need some modifications to the generated vfio.rs. 59 | First change below line: 60 | ```rust 61 | pub const VFIO_TYPE: u8 = 59u8; 62 | ``` 63 | to 64 | ```rust 65 | pub const VFIO_TYPE: u32 = 59; 66 | ``` 67 | 68 | This is required due to that bindgen can not generate VFIO_TYPE correctly 69 | at this moment. You might also want to add the proper license header to 70 | the file. 71 | 72 | Finally add the new version module to `vfio-bindings/lib.rs`. If this version 73 | is newer than the others already present, make this version the default one by 74 | getting it imported when there isn't any other version specified as a feature: 75 | 76 | ```rust 77 | #[cfg(all(not(feature = "vfio-v5_0_0"), not(feature = "vfio-v5_2_0")))] 78 | pub use super::bindings_v5_2_0::*; 79 | ``` 80 | -------------------------------------------------------------------------------- /crates/vfio-bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfio-bindings" 3 | version = "0.3.0" 4 | authors = ["The Cloud Hypervisor Authors"] 5 | license = "Apache-2.0 OR BSD-3-Clause" 6 | description = "Rust FFI bindings to vfio generated using bindgen." 7 | repository = "https://github.com/rust-vmm/vfio" 8 | readme = "README.md" 9 | edition = "2018" 10 | keywords = ["vfio"] 11 | 12 | [features] 13 | vfio-v5_0_0 = [] 14 | fam-wrappers = ["vmm-sys-util"] 15 | 16 | [dependencies] 17 | vmm-sys-util = { version = ">=0.8.0", optional = true } 18 | 19 | [dev-dependencies] 20 | byteorder = ">=1.2.1" 21 | -------------------------------------------------------------------------------- /crates/vfio-bindings/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /crates/vfio-bindings/LICENSE-BSD: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright 2019 Intel Corporation. All Rights Reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /crates/vfio-bindings/README.md: -------------------------------------------------------------------------------- 1 | # vfio-bindings 2 | 3 | ## Design 4 | 5 | The vfio-bindings crate is designed as rust FFI bindings to vfio 6 | generated using [bindgen](https://crates.io/crates/bindgen). 7 | 8 | Multiple Linux versions are supported through rust 'features'. For each 9 | supported Linux version, a feature is introduced. 10 | 11 | Currently supported features/Linux versions: 12 | - vfio-v5_0_0 contains the bindings for the Linux kernel version 5.0 13 | 14 | ## Usage 15 | 16 | First, add the following to your Cargo.toml: 17 | ```toml 18 | vfio-bindings = "0.3" 19 | ``` 20 | Next, add this to your crate root: 21 | 22 | ```rust 23 | extern crate vfio_bindings; 24 | ``` 25 | 26 | By default vfio-bindings will export a wrapper over the latest available kernel 27 | version it supported, but you can select a different version by specifying it in 28 | your Cargo.toml: 29 | ```toml 30 | vfio-bindings = { version = "0.3", features = ["vfio-v5_0_0"]} 31 | ``` 32 | 33 | ## Examples 34 | 35 | To use this bindings, you can do: 36 | ```rust 37 | use vfio_bindings::bindings::vfio::*; 38 | ``` 39 | 40 | ## License 41 | 42 | This code is licensed under Apache-2.0 or BSD-3-Clause. 43 | -------------------------------------------------------------------------------- /crates/vfio-bindings/src/bindings_v5_0_0/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Intel Corporation. All Rights Reserved. 2 | // SPDX-License-Identifier: (BSD-3-Clause OR Apache-2.0) 3 | 4 | #![allow(clippy::all)] 5 | #![allow(non_upper_case_globals)] 6 | #![allow(non_camel_case_types)] 7 | #![allow(non_snake_case)] 8 | 9 | pub mod vfio; 10 | -------------------------------------------------------------------------------- /crates/vfio-bindings/src/bindings_v5_0_0/vfio.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen */ 2 | 3 | #[repr(C)] 4 | #[derive(Default)] 5 | pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); 6 | impl __IncompleteArrayField { 7 | #[inline] 8 | pub const fn new() -> Self { 9 | __IncompleteArrayField(::std::marker::PhantomData, []) 10 | } 11 | #[inline] 12 | pub unsafe fn as_ptr(&self) -> *const T { 13 | ::std::mem::transmute(self) 14 | } 15 | #[inline] 16 | pub unsafe fn as_mut_ptr(&mut self) -> *mut T { 17 | ::std::mem::transmute(self) 18 | } 19 | #[inline] 20 | pub unsafe fn as_slice(&self, len: usize) -> &[T] { 21 | ::std::slice::from_raw_parts(self.as_ptr(), len) 22 | } 23 | #[inline] 24 | pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { 25 | ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) 26 | } 27 | } 28 | impl ::std::fmt::Debug for __IncompleteArrayField { 29 | fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 30 | fmt.write_str("__IncompleteArrayField") 31 | } 32 | } 33 | impl ::std::clone::Clone for __IncompleteArrayField { 34 | #[inline] 35 | fn clone(&self) -> Self { 36 | Self::new() 37 | } 38 | } 39 | pub const __BITS_PER_LONG: u32 = 64; 40 | pub const __FD_SETSIZE: u32 = 1024; 41 | pub const _IOC_NRBITS: u32 = 8; 42 | pub const _IOC_TYPEBITS: u32 = 8; 43 | pub const _IOC_SIZEBITS: u32 = 14; 44 | pub const _IOC_DIRBITS: u32 = 2; 45 | pub const _IOC_NRMASK: u32 = 255; 46 | pub const _IOC_TYPEMASK: u32 = 255; 47 | pub const _IOC_SIZEMASK: u32 = 16383; 48 | pub const _IOC_DIRMASK: u32 = 3; 49 | pub const _IOC_NRSHIFT: u32 = 0; 50 | pub const _IOC_TYPESHIFT: u32 = 8; 51 | pub const _IOC_SIZESHIFT: u32 = 16; 52 | pub const _IOC_DIRSHIFT: u32 = 30; 53 | pub const _IOC_NONE: u32 = 0; 54 | pub const _IOC_WRITE: u32 = 1; 55 | pub const _IOC_READ: u32 = 2; 56 | pub const IOC_IN: u32 = 1073741824; 57 | pub const IOC_OUT: u32 = 2147483648; 58 | pub const IOC_INOUT: u32 = 3221225472; 59 | pub const IOCSIZE_MASK: u32 = 1073676288; 60 | pub const IOCSIZE_SHIFT: u32 = 16; 61 | pub const VFIO_API_VERSION: u32 = 0; 62 | pub const VFIO_TYPE1_IOMMU: u32 = 1; 63 | pub const VFIO_SPAPR_TCE_IOMMU: u32 = 2; 64 | pub const VFIO_TYPE1v2_IOMMU: u32 = 3; 65 | pub const VFIO_DMA_CC_IOMMU: u32 = 4; 66 | pub const VFIO_EEH: u32 = 5; 67 | pub const VFIO_TYPE1_NESTING_IOMMU: u32 = 6; 68 | pub const VFIO_SPAPR_TCE_v2_IOMMU: u32 = 7; 69 | pub const VFIO_NOIOMMU_IOMMU: u32 = 8; 70 | pub const VFIO_TYPE: u32 = 59; 71 | pub const VFIO_BASE: u32 = 100; 72 | pub const VFIO_GROUP_FLAGS_VIABLE: u32 = 1; 73 | pub const VFIO_GROUP_FLAGS_CONTAINER_SET: u32 = 2; 74 | pub const VFIO_DEVICE_FLAGS_RESET: u32 = 1; 75 | pub const VFIO_DEVICE_FLAGS_PCI: u32 = 2; 76 | pub const VFIO_DEVICE_FLAGS_PLATFORM: u32 = 4; 77 | pub const VFIO_DEVICE_FLAGS_AMBA: u32 = 8; 78 | pub const VFIO_DEVICE_FLAGS_CCW: u32 = 16; 79 | pub const VFIO_DEVICE_FLAGS_AP: u32 = 32; 80 | pub const VFIO_DEVICE_API_PCI_STRING: &'static [u8; 9usize] = b"vfio-pci\0"; 81 | pub const VFIO_DEVICE_API_PLATFORM_STRING: &'static [u8; 14usize] = b"vfio-platform\0"; 82 | pub const VFIO_DEVICE_API_AMBA_STRING: &'static [u8; 10usize] = b"vfio-amba\0"; 83 | pub const VFIO_DEVICE_API_CCW_STRING: &'static [u8; 9usize] = b"vfio-ccw\0"; 84 | pub const VFIO_DEVICE_API_AP_STRING: &'static [u8; 8usize] = b"vfio-ap\0"; 85 | pub const VFIO_REGION_INFO_FLAG_READ: u32 = 1; 86 | pub const VFIO_REGION_INFO_FLAG_WRITE: u32 = 2; 87 | pub const VFIO_REGION_INFO_FLAG_MMAP: u32 = 4; 88 | pub const VFIO_REGION_INFO_FLAG_CAPS: u32 = 8; 89 | pub const VFIO_REGION_INFO_CAP_SPARSE_MMAP: u32 = 1; 90 | pub const VFIO_REGION_INFO_CAP_TYPE: u32 = 2; 91 | pub const VFIO_REGION_TYPE_PCI_VENDOR_TYPE: u32 = 2147483648; 92 | pub const VFIO_REGION_TYPE_PCI_VENDOR_MASK: u32 = 65535; 93 | pub const VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION: u32 = 1; 94 | pub const VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG: u32 = 2; 95 | pub const VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG: u32 = 3; 96 | pub const VFIO_REGION_TYPE_GFX: u32 = 1; 97 | pub const VFIO_REGION_SUBTYPE_GFX_EDID: u32 = 1; 98 | pub const VFIO_DEVICE_GFX_LINK_STATE_UP: u32 = 1; 99 | pub const VFIO_DEVICE_GFX_LINK_STATE_DOWN: u32 = 2; 100 | pub const VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM: u32 = 1; 101 | pub const VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD: u32 = 1; 102 | pub const VFIO_REGION_INFO_CAP_MSIX_MAPPABLE: u32 = 3; 103 | pub const VFIO_REGION_INFO_CAP_NVLINK2_SSATGT: u32 = 4; 104 | pub const VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD: u32 = 5; 105 | pub const VFIO_IRQ_INFO_EVENTFD: u32 = 1; 106 | pub const VFIO_IRQ_INFO_MASKABLE: u32 = 2; 107 | pub const VFIO_IRQ_INFO_AUTOMASKED: u32 = 4; 108 | pub const VFIO_IRQ_INFO_NORESIZE: u32 = 8; 109 | pub const VFIO_IRQ_SET_DATA_NONE: u32 = 1; 110 | pub const VFIO_IRQ_SET_DATA_BOOL: u32 = 2; 111 | pub const VFIO_IRQ_SET_DATA_EVENTFD: u32 = 4; 112 | pub const VFIO_IRQ_SET_ACTION_MASK: u32 = 8; 113 | pub const VFIO_IRQ_SET_ACTION_UNMASK: u32 = 16; 114 | pub const VFIO_IRQ_SET_ACTION_TRIGGER: u32 = 32; 115 | pub const VFIO_IRQ_SET_DATA_TYPE_MASK: u32 = 7; 116 | pub const VFIO_IRQ_SET_ACTION_TYPE_MASK: u32 = 56; 117 | pub const VFIO_GFX_PLANE_TYPE_PROBE: u32 = 1; 118 | pub const VFIO_GFX_PLANE_TYPE_DMABUF: u32 = 2; 119 | pub const VFIO_GFX_PLANE_TYPE_REGION: u32 = 4; 120 | pub const VFIO_DEVICE_IOEVENTFD_8: u32 = 1; 121 | pub const VFIO_DEVICE_IOEVENTFD_16: u32 = 2; 122 | pub const VFIO_DEVICE_IOEVENTFD_32: u32 = 4; 123 | pub const VFIO_DEVICE_IOEVENTFD_64: u32 = 8; 124 | pub const VFIO_DEVICE_IOEVENTFD_SIZE_MASK: u32 = 15; 125 | pub const VFIO_IOMMU_INFO_PGSIZES: u32 = 1; 126 | pub const VFIO_DMA_MAP_FLAG_READ: u32 = 1; 127 | pub const VFIO_DMA_MAP_FLAG_WRITE: u32 = 2; 128 | pub const VFIO_IOMMU_SPAPR_INFO_DDW: u32 = 1; 129 | pub const VFIO_EEH_PE_DISABLE: u32 = 0; 130 | pub const VFIO_EEH_PE_ENABLE: u32 = 1; 131 | pub const VFIO_EEH_PE_UNFREEZE_IO: u32 = 2; 132 | pub const VFIO_EEH_PE_UNFREEZE_DMA: u32 = 3; 133 | pub const VFIO_EEH_PE_GET_STATE: u32 = 4; 134 | pub const VFIO_EEH_PE_STATE_NORMAL: u32 = 0; 135 | pub const VFIO_EEH_PE_STATE_RESET: u32 = 1; 136 | pub const VFIO_EEH_PE_STATE_STOPPED: u32 = 2; 137 | pub const VFIO_EEH_PE_STATE_STOPPED_DMA: u32 = 4; 138 | pub const VFIO_EEH_PE_STATE_UNAVAIL: u32 = 5; 139 | pub const VFIO_EEH_PE_RESET_DEACTIVATE: u32 = 5; 140 | pub const VFIO_EEH_PE_RESET_HOT: u32 = 6; 141 | pub const VFIO_EEH_PE_RESET_FUNDAMENTAL: u32 = 7; 142 | pub const VFIO_EEH_PE_CONFIGURE: u32 = 8; 143 | pub const VFIO_EEH_PE_INJECT_ERR: u32 = 9; 144 | pub type __s8 = ::std::os::raw::c_schar; 145 | pub type __u8 = ::std::os::raw::c_uchar; 146 | pub type __s16 = ::std::os::raw::c_short; 147 | pub type __u16 = ::std::os::raw::c_ushort; 148 | pub type __s32 = ::std::os::raw::c_int; 149 | pub type __u32 = ::std::os::raw::c_uint; 150 | pub type __s64 = ::std::os::raw::c_longlong; 151 | pub type __u64 = ::std::os::raw::c_ulonglong; 152 | #[repr(C)] 153 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 154 | pub struct __kernel_fd_set { 155 | pub fds_bits: [::std::os::raw::c_ulong; 16usize], 156 | } 157 | #[test] 158 | fn bindgen_test_layout___kernel_fd_set() { 159 | assert_eq!( 160 | ::std::mem::size_of::<__kernel_fd_set>(), 161 | 128usize, 162 | concat!("Size of: ", stringify!(__kernel_fd_set)) 163 | ); 164 | assert_eq!( 165 | ::std::mem::align_of::<__kernel_fd_set>(), 166 | 8usize, 167 | concat!("Alignment of ", stringify!(__kernel_fd_set)) 168 | ); 169 | assert_eq!( 170 | unsafe { &(*(::std::ptr::null::<__kernel_fd_set>())).fds_bits as *const _ as usize }, 171 | 0usize, 172 | concat!( 173 | "Offset of field: ", 174 | stringify!(__kernel_fd_set), 175 | "::", 176 | stringify!(fds_bits) 177 | ) 178 | ); 179 | } 180 | pub type __kernel_sighandler_t = 181 | ::std::option::Option; 182 | pub type __kernel_key_t = ::std::os::raw::c_int; 183 | pub type __kernel_mqd_t = ::std::os::raw::c_int; 184 | pub type __kernel_old_uid_t = ::std::os::raw::c_ushort; 185 | pub type __kernel_old_gid_t = ::std::os::raw::c_ushort; 186 | pub type __kernel_old_dev_t = ::std::os::raw::c_ulong; 187 | pub type __kernel_long_t = ::std::os::raw::c_long; 188 | pub type __kernel_ulong_t = ::std::os::raw::c_ulong; 189 | pub type __kernel_ino_t = __kernel_ulong_t; 190 | pub type __kernel_mode_t = ::std::os::raw::c_uint; 191 | pub type __kernel_pid_t = ::std::os::raw::c_int; 192 | pub type __kernel_ipc_pid_t = ::std::os::raw::c_int; 193 | pub type __kernel_uid_t = ::std::os::raw::c_uint; 194 | pub type __kernel_gid_t = ::std::os::raw::c_uint; 195 | pub type __kernel_suseconds_t = __kernel_long_t; 196 | pub type __kernel_daddr_t = ::std::os::raw::c_int; 197 | pub type __kernel_uid32_t = ::std::os::raw::c_uint; 198 | pub type __kernel_gid32_t = ::std::os::raw::c_uint; 199 | pub type __kernel_size_t = __kernel_ulong_t; 200 | pub type __kernel_ssize_t = __kernel_long_t; 201 | pub type __kernel_ptrdiff_t = __kernel_long_t; 202 | #[repr(C)] 203 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 204 | pub struct __kernel_fsid_t { 205 | pub val: [::std::os::raw::c_int; 2usize], 206 | } 207 | #[test] 208 | fn bindgen_test_layout___kernel_fsid_t() { 209 | assert_eq!( 210 | ::std::mem::size_of::<__kernel_fsid_t>(), 211 | 8usize, 212 | concat!("Size of: ", stringify!(__kernel_fsid_t)) 213 | ); 214 | assert_eq!( 215 | ::std::mem::align_of::<__kernel_fsid_t>(), 216 | 4usize, 217 | concat!("Alignment of ", stringify!(__kernel_fsid_t)) 218 | ); 219 | assert_eq!( 220 | unsafe { &(*(::std::ptr::null::<__kernel_fsid_t>())).val as *const _ as usize }, 221 | 0usize, 222 | concat!( 223 | "Offset of field: ", 224 | stringify!(__kernel_fsid_t), 225 | "::", 226 | stringify!(val) 227 | ) 228 | ); 229 | } 230 | pub type __kernel_off_t = __kernel_long_t; 231 | pub type __kernel_loff_t = ::std::os::raw::c_longlong; 232 | pub type __kernel_time_t = __kernel_long_t; 233 | pub type __kernel_time64_t = ::std::os::raw::c_longlong; 234 | pub type __kernel_clock_t = __kernel_long_t; 235 | pub type __kernel_timer_t = ::std::os::raw::c_int; 236 | pub type __kernel_clockid_t = ::std::os::raw::c_int; 237 | pub type __kernel_caddr_t = *mut ::std::os::raw::c_char; 238 | pub type __kernel_uid16_t = ::std::os::raw::c_ushort; 239 | pub type __kernel_gid16_t = ::std::os::raw::c_ushort; 240 | pub type __le16 = __u16; 241 | pub type __be16 = __u16; 242 | pub type __le32 = __u32; 243 | pub type __be32 = __u32; 244 | pub type __le64 = __u64; 245 | pub type __be64 = __u64; 246 | pub type __sum16 = __u16; 247 | pub type __wsum = __u32; 248 | pub type __poll_t = ::std::os::raw::c_uint; 249 | #[repr(C)] 250 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 251 | pub struct vfio_info_cap_header { 252 | pub id: __u16, 253 | pub version: __u16, 254 | pub next: __u32, 255 | } 256 | #[test] 257 | fn bindgen_test_layout_vfio_info_cap_header() { 258 | assert_eq!( 259 | ::std::mem::size_of::(), 260 | 8usize, 261 | concat!("Size of: ", stringify!(vfio_info_cap_header)) 262 | ); 263 | assert_eq!( 264 | ::std::mem::align_of::(), 265 | 4usize, 266 | concat!("Alignment of ", stringify!(vfio_info_cap_header)) 267 | ); 268 | assert_eq!( 269 | unsafe { &(*(::std::ptr::null::())).id as *const _ as usize }, 270 | 0usize, 271 | concat!( 272 | "Offset of field: ", 273 | stringify!(vfio_info_cap_header), 274 | "::", 275 | stringify!(id) 276 | ) 277 | ); 278 | assert_eq!( 279 | unsafe { &(*(::std::ptr::null::())).version as *const _ as usize }, 280 | 2usize, 281 | concat!( 282 | "Offset of field: ", 283 | stringify!(vfio_info_cap_header), 284 | "::", 285 | stringify!(version) 286 | ) 287 | ); 288 | assert_eq!( 289 | unsafe { &(*(::std::ptr::null::())).next as *const _ as usize }, 290 | 4usize, 291 | concat!( 292 | "Offset of field: ", 293 | stringify!(vfio_info_cap_header), 294 | "::", 295 | stringify!(next) 296 | ) 297 | ); 298 | } 299 | #[doc = " VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3,"] 300 | #[doc = "\t\t\t\t\t\tstruct vfio_group_status)"] 301 | #[doc = ""] 302 | #[doc = " Retrieve information about the group. Fills in provided"] 303 | #[doc = " struct vfio_group_info. Caller sets argsz."] 304 | #[doc = " Return: 0 on succes, -errno on failure."] 305 | #[doc = " Availability: Always"] 306 | #[repr(C)] 307 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 308 | pub struct vfio_group_status { 309 | pub argsz: __u32, 310 | pub flags: __u32, 311 | } 312 | #[test] 313 | fn bindgen_test_layout_vfio_group_status() { 314 | assert_eq!( 315 | ::std::mem::size_of::(), 316 | 8usize, 317 | concat!("Size of: ", stringify!(vfio_group_status)) 318 | ); 319 | assert_eq!( 320 | ::std::mem::align_of::(), 321 | 4usize, 322 | concat!("Alignment of ", stringify!(vfio_group_status)) 323 | ); 324 | assert_eq!( 325 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 326 | 0usize, 327 | concat!( 328 | "Offset of field: ", 329 | stringify!(vfio_group_status), 330 | "::", 331 | stringify!(argsz) 332 | ) 333 | ); 334 | assert_eq!( 335 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 336 | 4usize, 337 | concat!( 338 | "Offset of field: ", 339 | stringify!(vfio_group_status), 340 | "::", 341 | stringify!(flags) 342 | ) 343 | ); 344 | } 345 | #[doc = " VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7,"] 346 | #[doc = "\t\t\t\t\t\tstruct vfio_device_info)"] 347 | #[doc = ""] 348 | #[doc = " Retrieve information about the device. Fills in provided"] 349 | #[doc = " struct vfio_device_info. Caller sets argsz."] 350 | #[doc = " Return: 0 on success, -errno on failure."] 351 | #[repr(C)] 352 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 353 | pub struct vfio_device_info { 354 | pub argsz: __u32, 355 | pub flags: __u32, 356 | pub num_regions: __u32, 357 | pub num_irqs: __u32, 358 | } 359 | #[test] 360 | fn bindgen_test_layout_vfio_device_info() { 361 | assert_eq!( 362 | ::std::mem::size_of::(), 363 | 16usize, 364 | concat!("Size of: ", stringify!(vfio_device_info)) 365 | ); 366 | assert_eq!( 367 | ::std::mem::align_of::(), 368 | 4usize, 369 | concat!("Alignment of ", stringify!(vfio_device_info)) 370 | ); 371 | assert_eq!( 372 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 373 | 0usize, 374 | concat!( 375 | "Offset of field: ", 376 | stringify!(vfio_device_info), 377 | "::", 378 | stringify!(argsz) 379 | ) 380 | ); 381 | assert_eq!( 382 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 383 | 4usize, 384 | concat!( 385 | "Offset of field: ", 386 | stringify!(vfio_device_info), 387 | "::", 388 | stringify!(flags) 389 | ) 390 | ); 391 | assert_eq!( 392 | unsafe { &(*(::std::ptr::null::())).num_regions as *const _ as usize }, 393 | 8usize, 394 | concat!( 395 | "Offset of field: ", 396 | stringify!(vfio_device_info), 397 | "::", 398 | stringify!(num_regions) 399 | ) 400 | ); 401 | assert_eq!( 402 | unsafe { &(*(::std::ptr::null::())).num_irqs as *const _ as usize }, 403 | 12usize, 404 | concat!( 405 | "Offset of field: ", 406 | stringify!(vfio_device_info), 407 | "::", 408 | stringify!(num_irqs) 409 | ) 410 | ); 411 | } 412 | #[doc = " VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8,"] 413 | #[doc = "\t\t\t\t struct vfio_region_info)"] 414 | #[doc = ""] 415 | #[doc = " Retrieve information about a device region. Caller provides"] 416 | #[doc = " struct vfio_region_info with index value set. Caller sets argsz."] 417 | #[doc = " Implementation of region mapping is bus driver specific. This is"] 418 | #[doc = " intended to describe MMIO, I/O port, as well as bus specific"] 419 | #[doc = " regions (ex. PCI config space). Zero sized regions may be used"] 420 | #[doc = " to describe unimplemented regions (ex. unimplemented PCI BARs)."] 421 | #[doc = " Return: 0 on success, -errno on failure."] 422 | #[repr(C)] 423 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 424 | pub struct vfio_region_info { 425 | pub argsz: __u32, 426 | pub flags: __u32, 427 | pub index: __u32, 428 | pub cap_offset: __u32, 429 | pub size: __u64, 430 | pub offset: __u64, 431 | } 432 | #[test] 433 | fn bindgen_test_layout_vfio_region_info() { 434 | assert_eq!( 435 | ::std::mem::size_of::(), 436 | 32usize, 437 | concat!("Size of: ", stringify!(vfio_region_info)) 438 | ); 439 | assert_eq!( 440 | ::std::mem::align_of::(), 441 | 8usize, 442 | concat!("Alignment of ", stringify!(vfio_region_info)) 443 | ); 444 | assert_eq!( 445 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 446 | 0usize, 447 | concat!( 448 | "Offset of field: ", 449 | stringify!(vfio_region_info), 450 | "::", 451 | stringify!(argsz) 452 | ) 453 | ); 454 | assert_eq!( 455 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 456 | 4usize, 457 | concat!( 458 | "Offset of field: ", 459 | stringify!(vfio_region_info), 460 | "::", 461 | stringify!(flags) 462 | ) 463 | ); 464 | assert_eq!( 465 | unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, 466 | 8usize, 467 | concat!( 468 | "Offset of field: ", 469 | stringify!(vfio_region_info), 470 | "::", 471 | stringify!(index) 472 | ) 473 | ); 474 | assert_eq!( 475 | unsafe { &(*(::std::ptr::null::())).cap_offset as *const _ as usize }, 476 | 12usize, 477 | concat!( 478 | "Offset of field: ", 479 | stringify!(vfio_region_info), 480 | "::", 481 | stringify!(cap_offset) 482 | ) 483 | ); 484 | assert_eq!( 485 | unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, 486 | 16usize, 487 | concat!( 488 | "Offset of field: ", 489 | stringify!(vfio_region_info), 490 | "::", 491 | stringify!(size) 492 | ) 493 | ); 494 | assert_eq!( 495 | unsafe { &(*(::std::ptr::null::())).offset as *const _ as usize }, 496 | 24usize, 497 | concat!( 498 | "Offset of field: ", 499 | stringify!(vfio_region_info), 500 | "::", 501 | stringify!(offset) 502 | ) 503 | ); 504 | } 505 | #[repr(C)] 506 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 507 | pub struct vfio_region_sparse_mmap_area { 508 | pub offset: __u64, 509 | pub size: __u64, 510 | } 511 | #[test] 512 | fn bindgen_test_layout_vfio_region_sparse_mmap_area() { 513 | assert_eq!( 514 | ::std::mem::size_of::(), 515 | 16usize, 516 | concat!("Size of: ", stringify!(vfio_region_sparse_mmap_area)) 517 | ); 518 | assert_eq!( 519 | ::std::mem::align_of::(), 520 | 8usize, 521 | concat!("Alignment of ", stringify!(vfio_region_sparse_mmap_area)) 522 | ); 523 | assert_eq!( 524 | unsafe { 525 | &(*(::std::ptr::null::())).offset as *const _ as usize 526 | }, 527 | 0usize, 528 | concat!( 529 | "Offset of field: ", 530 | stringify!(vfio_region_sparse_mmap_area), 531 | "::", 532 | stringify!(offset) 533 | ) 534 | ); 535 | assert_eq!( 536 | unsafe { 537 | &(*(::std::ptr::null::())).size as *const _ as usize 538 | }, 539 | 8usize, 540 | concat!( 541 | "Offset of field: ", 542 | stringify!(vfio_region_sparse_mmap_area), 543 | "::", 544 | stringify!(size) 545 | ) 546 | ); 547 | } 548 | #[repr(C)] 549 | #[derive(Debug, Default)] 550 | pub struct vfio_region_info_cap_sparse_mmap { 551 | pub header: vfio_info_cap_header, 552 | pub nr_areas: __u32, 553 | pub reserved: __u32, 554 | pub areas: __IncompleteArrayField, 555 | } 556 | #[test] 557 | fn bindgen_test_layout_vfio_region_info_cap_sparse_mmap() { 558 | assert_eq!( 559 | ::std::mem::size_of::(), 560 | 16usize, 561 | concat!("Size of: ", stringify!(vfio_region_info_cap_sparse_mmap)) 562 | ); 563 | assert_eq!( 564 | ::std::mem::align_of::(), 565 | 8usize, 566 | concat!( 567 | "Alignment of ", 568 | stringify!(vfio_region_info_cap_sparse_mmap) 569 | ) 570 | ); 571 | } 572 | #[repr(C)] 573 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 574 | pub struct vfio_region_info_cap_type { 575 | pub header: vfio_info_cap_header, 576 | pub type_: __u32, 577 | pub subtype: __u32, 578 | } 579 | #[test] 580 | fn bindgen_test_layout_vfio_region_info_cap_type() { 581 | assert_eq!( 582 | ::std::mem::size_of::(), 583 | 16usize, 584 | concat!("Size of: ", stringify!(vfio_region_info_cap_type)) 585 | ); 586 | assert_eq!( 587 | ::std::mem::align_of::(), 588 | 4usize, 589 | concat!("Alignment of ", stringify!(vfio_region_info_cap_type)) 590 | ); 591 | assert_eq!( 592 | unsafe { 593 | &(*(::std::ptr::null::())).header as *const _ as usize 594 | }, 595 | 0usize, 596 | concat!( 597 | "Offset of field: ", 598 | stringify!(vfio_region_info_cap_type), 599 | "::", 600 | stringify!(header) 601 | ) 602 | ); 603 | assert_eq!( 604 | unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, 605 | 8usize, 606 | concat!( 607 | "Offset of field: ", 608 | stringify!(vfio_region_info_cap_type), 609 | "::", 610 | stringify!(type_) 611 | ) 612 | ); 613 | assert_eq!( 614 | unsafe { 615 | &(*(::std::ptr::null::())).subtype as *const _ as usize 616 | }, 617 | 12usize, 618 | concat!( 619 | "Offset of field: ", 620 | stringify!(vfio_region_info_cap_type), 621 | "::", 622 | stringify!(subtype) 623 | ) 624 | ); 625 | } 626 | #[doc = " struct vfio_region_gfx_edid - EDID region layout."] 627 | #[doc = ""] 628 | #[doc = " Set display link state and EDID blob."] 629 | #[doc = ""] 630 | #[doc = " The EDID blob has monitor information such as brand, name, serial"] 631 | #[doc = " number, physical size, supported video modes and more."] 632 | #[doc = ""] 633 | #[doc = " This special region allows userspace (typically qemu) set a virtual"] 634 | #[doc = " EDID for the virtual monitor, which allows a flexible display"] 635 | #[doc = " configuration."] 636 | #[doc = ""] 637 | #[doc = " For the edid blob spec look here:"] 638 | #[doc = " https://en.wikipedia.org/wiki/Extended_Display_Identification_Data"] 639 | #[doc = ""] 640 | #[doc = " On linux systems you can find the EDID blob in sysfs:"] 641 | #[doc = " /sys/class/drm/${card}/${connector}/edid"] 642 | #[doc = ""] 643 | #[doc = " You can use the edid-decode ulility (comes with xorg-x11-utils) to"] 644 | #[doc = " decode the EDID blob."] 645 | #[doc = ""] 646 | #[doc = " @edid_offset: location of the edid blob, relative to the"] 647 | #[doc = " start of the region (readonly)."] 648 | #[doc = " @edid_max_size: max size of the edid blob (readonly)."] 649 | #[doc = " @edid_size: actual edid size (read/write)."] 650 | #[doc = " @link_state: display link state (read/write)."] 651 | #[doc = " VFIO_DEVICE_GFX_LINK_STATE_UP: Monitor is turned on."] 652 | #[doc = " VFIO_DEVICE_GFX_LINK_STATE_DOWN: Monitor is turned off."] 653 | #[doc = " @max_xres: max display width (0 == no limitation, readonly)."] 654 | #[doc = " @max_yres: max display height (0 == no limitation, readonly)."] 655 | #[doc = ""] 656 | #[doc = " EDID update protocol:"] 657 | #[doc = " (1) set link-state to down."] 658 | #[doc = " (2) update edid blob and size."] 659 | #[doc = " (3) set link-state to up."] 660 | #[repr(C)] 661 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 662 | pub struct vfio_region_gfx_edid { 663 | pub edid_offset: __u32, 664 | pub edid_max_size: __u32, 665 | pub edid_size: __u32, 666 | pub max_xres: __u32, 667 | pub max_yres: __u32, 668 | pub link_state: __u32, 669 | } 670 | #[test] 671 | fn bindgen_test_layout_vfio_region_gfx_edid() { 672 | assert_eq!( 673 | ::std::mem::size_of::(), 674 | 24usize, 675 | concat!("Size of: ", stringify!(vfio_region_gfx_edid)) 676 | ); 677 | assert_eq!( 678 | ::std::mem::align_of::(), 679 | 4usize, 680 | concat!("Alignment of ", stringify!(vfio_region_gfx_edid)) 681 | ); 682 | assert_eq!( 683 | unsafe { 684 | &(*(::std::ptr::null::())).edid_offset as *const _ as usize 685 | }, 686 | 0usize, 687 | concat!( 688 | "Offset of field: ", 689 | stringify!(vfio_region_gfx_edid), 690 | "::", 691 | stringify!(edid_offset) 692 | ) 693 | ); 694 | assert_eq!( 695 | unsafe { 696 | &(*(::std::ptr::null::())).edid_max_size as *const _ as usize 697 | }, 698 | 4usize, 699 | concat!( 700 | "Offset of field: ", 701 | stringify!(vfio_region_gfx_edid), 702 | "::", 703 | stringify!(edid_max_size) 704 | ) 705 | ); 706 | assert_eq!( 707 | unsafe { &(*(::std::ptr::null::())).edid_size as *const _ as usize }, 708 | 8usize, 709 | concat!( 710 | "Offset of field: ", 711 | stringify!(vfio_region_gfx_edid), 712 | "::", 713 | stringify!(edid_size) 714 | ) 715 | ); 716 | assert_eq!( 717 | unsafe { &(*(::std::ptr::null::())).max_xres as *const _ as usize }, 718 | 12usize, 719 | concat!( 720 | "Offset of field: ", 721 | stringify!(vfio_region_gfx_edid), 722 | "::", 723 | stringify!(max_xres) 724 | ) 725 | ); 726 | assert_eq!( 727 | unsafe { &(*(::std::ptr::null::())).max_yres as *const _ as usize }, 728 | 16usize, 729 | concat!( 730 | "Offset of field: ", 731 | stringify!(vfio_region_gfx_edid), 732 | "::", 733 | stringify!(max_yres) 734 | ) 735 | ); 736 | assert_eq!( 737 | unsafe { &(*(::std::ptr::null::())).link_state as *const _ as usize }, 738 | 20usize, 739 | concat!( 740 | "Offset of field: ", 741 | stringify!(vfio_region_gfx_edid), 742 | "::", 743 | stringify!(link_state) 744 | ) 745 | ); 746 | } 747 | #[repr(C)] 748 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 749 | pub struct vfio_region_info_cap_nvlink2_ssatgt { 750 | pub header: vfio_info_cap_header, 751 | pub tgt: __u64, 752 | } 753 | #[test] 754 | fn bindgen_test_layout_vfio_region_info_cap_nvlink2_ssatgt() { 755 | assert_eq!( 756 | ::std::mem::size_of::(), 757 | 16usize, 758 | concat!("Size of: ", stringify!(vfio_region_info_cap_nvlink2_ssatgt)) 759 | ); 760 | assert_eq!( 761 | ::std::mem::align_of::(), 762 | 8usize, 763 | concat!( 764 | "Alignment of ", 765 | stringify!(vfio_region_info_cap_nvlink2_ssatgt) 766 | ) 767 | ); 768 | assert_eq!( 769 | unsafe { 770 | &(*(::std::ptr::null::())).header as *const _ 771 | as usize 772 | }, 773 | 0usize, 774 | concat!( 775 | "Offset of field: ", 776 | stringify!(vfio_region_info_cap_nvlink2_ssatgt), 777 | "::", 778 | stringify!(header) 779 | ) 780 | ); 781 | assert_eq!( 782 | unsafe { 783 | &(*(::std::ptr::null::())).tgt as *const _ as usize 784 | }, 785 | 8usize, 786 | concat!( 787 | "Offset of field: ", 788 | stringify!(vfio_region_info_cap_nvlink2_ssatgt), 789 | "::", 790 | stringify!(tgt) 791 | ) 792 | ); 793 | } 794 | #[repr(C)] 795 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 796 | pub struct vfio_region_info_cap_nvlink2_lnkspd { 797 | pub header: vfio_info_cap_header, 798 | pub link_speed: __u32, 799 | pub __pad: __u32, 800 | } 801 | #[test] 802 | fn bindgen_test_layout_vfio_region_info_cap_nvlink2_lnkspd() { 803 | assert_eq!( 804 | ::std::mem::size_of::(), 805 | 16usize, 806 | concat!("Size of: ", stringify!(vfio_region_info_cap_nvlink2_lnkspd)) 807 | ); 808 | assert_eq!( 809 | ::std::mem::align_of::(), 810 | 4usize, 811 | concat!( 812 | "Alignment of ", 813 | stringify!(vfio_region_info_cap_nvlink2_lnkspd) 814 | ) 815 | ); 816 | assert_eq!( 817 | unsafe { 818 | &(*(::std::ptr::null::())).header as *const _ 819 | as usize 820 | }, 821 | 0usize, 822 | concat!( 823 | "Offset of field: ", 824 | stringify!(vfio_region_info_cap_nvlink2_lnkspd), 825 | "::", 826 | stringify!(header) 827 | ) 828 | ); 829 | assert_eq!( 830 | unsafe { 831 | &(*(::std::ptr::null::())).link_speed as *const _ 832 | as usize 833 | }, 834 | 8usize, 835 | concat!( 836 | "Offset of field: ", 837 | stringify!(vfio_region_info_cap_nvlink2_lnkspd), 838 | "::", 839 | stringify!(link_speed) 840 | ) 841 | ); 842 | assert_eq!( 843 | unsafe { 844 | &(*(::std::ptr::null::())).__pad as *const _ 845 | as usize 846 | }, 847 | 12usize, 848 | concat!( 849 | "Offset of field: ", 850 | stringify!(vfio_region_info_cap_nvlink2_lnkspd), 851 | "::", 852 | stringify!(__pad) 853 | ) 854 | ); 855 | } 856 | #[doc = " VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,"] 857 | #[doc = "\t\t\t\t struct vfio_irq_info)"] 858 | #[doc = ""] 859 | #[doc = " Retrieve information about a device IRQ. Caller provides"] 860 | #[doc = " struct vfio_irq_info with index value set. Caller sets argsz."] 861 | #[doc = " Implementation of IRQ mapping is bus driver specific. Indexes"] 862 | #[doc = " using multiple IRQs are primarily intended to support MSI-like"] 863 | #[doc = " interrupt blocks. Zero count irq blocks may be used to describe"] 864 | #[doc = " unimplemented interrupt types."] 865 | #[doc = ""] 866 | #[doc = " The EVENTFD flag indicates the interrupt index supports eventfd based"] 867 | #[doc = " signaling."] 868 | #[doc = ""] 869 | #[doc = " The MASKABLE flags indicates the index supports MASK and UNMASK"] 870 | #[doc = " actions described below."] 871 | #[doc = ""] 872 | #[doc = " AUTOMASKED indicates that after signaling, the interrupt line is"] 873 | #[doc = " automatically masked by VFIO and the user needs to unmask the line"] 874 | #[doc = " to receive new interrupts. This is primarily intended to distinguish"] 875 | #[doc = " level triggered interrupts."] 876 | #[doc = ""] 877 | #[doc = " The NORESIZE flag indicates that the interrupt lines within the index"] 878 | #[doc = " are setup as a set and new subindexes cannot be enabled without first"] 879 | #[doc = " disabling the entire index. This is used for interrupts like PCI MSI"] 880 | #[doc = " and MSI-X where the driver may only use a subset of the available"] 881 | #[doc = " indexes, but VFIO needs to enable a specific number of vectors"] 882 | #[doc = " upfront. In the case of MSI-X, where the user can enable MSI-X and"] 883 | #[doc = " then add and unmask vectors, it's up to userspace to make the decision"] 884 | #[doc = " whether to allocate the maximum supported number of vectors or tear"] 885 | #[doc = " down setup and incrementally increase the vectors as each is enabled."] 886 | #[repr(C)] 887 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 888 | pub struct vfio_irq_info { 889 | pub argsz: __u32, 890 | pub flags: __u32, 891 | pub index: __u32, 892 | pub count: __u32, 893 | } 894 | #[test] 895 | fn bindgen_test_layout_vfio_irq_info() { 896 | assert_eq!( 897 | ::std::mem::size_of::(), 898 | 16usize, 899 | concat!("Size of: ", stringify!(vfio_irq_info)) 900 | ); 901 | assert_eq!( 902 | ::std::mem::align_of::(), 903 | 4usize, 904 | concat!("Alignment of ", stringify!(vfio_irq_info)) 905 | ); 906 | assert_eq!( 907 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 908 | 0usize, 909 | concat!( 910 | "Offset of field: ", 911 | stringify!(vfio_irq_info), 912 | "::", 913 | stringify!(argsz) 914 | ) 915 | ); 916 | assert_eq!( 917 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 918 | 4usize, 919 | concat!( 920 | "Offset of field: ", 921 | stringify!(vfio_irq_info), 922 | "::", 923 | stringify!(flags) 924 | ) 925 | ); 926 | assert_eq!( 927 | unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, 928 | 8usize, 929 | concat!( 930 | "Offset of field: ", 931 | stringify!(vfio_irq_info), 932 | "::", 933 | stringify!(index) 934 | ) 935 | ); 936 | assert_eq!( 937 | unsafe { &(*(::std::ptr::null::())).count as *const _ as usize }, 938 | 12usize, 939 | concat!( 940 | "Offset of field: ", 941 | stringify!(vfio_irq_info), 942 | "::", 943 | stringify!(count) 944 | ) 945 | ); 946 | } 947 | #[doc = " VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set)"] 948 | #[doc = ""] 949 | #[doc = " Set signaling, masking, and unmasking of interrupts. Caller provides"] 950 | #[doc = " struct vfio_irq_set with all fields set. 'start' and 'count' indicate"] 951 | #[doc = " the range of subindexes being specified."] 952 | #[doc = ""] 953 | #[doc = " The DATA flags specify the type of data provided. If DATA_NONE, the"] 954 | #[doc = " operation performs the specified action immediately on the specified"] 955 | #[doc = " interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]:"] 956 | #[doc = " flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1."] 957 | #[doc = ""] 958 | #[doc = " DATA_BOOL allows sparse support for the same on arrays of interrupts."] 959 | #[doc = " For example, to mask interrupts [0,1] and [0,3] (but not [0,2]):"] 960 | #[doc = " flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3,"] 961 | #[doc = " data = {1,0,1}"] 962 | #[doc = ""] 963 | #[doc = " DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd."] 964 | #[doc = " A value of -1 can be used to either de-assign interrupts if already"] 965 | #[doc = " assigned or skip un-assigned interrupts. For example, to set an eventfd"] 966 | #[doc = " to be trigger for interrupts [0,0] and [0,2]:"] 967 | #[doc = " flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3,"] 968 | #[doc = " data = {fd1, -1, fd2}"] 969 | #[doc = " If index [0,1] is previously set, two count = 1 ioctls calls would be"] 970 | #[doc = " required to set [0,0] and [0,2] without changing [0,1]."] 971 | #[doc = ""] 972 | #[doc = " Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used"] 973 | #[doc = " with ACTION_TRIGGER to perform kernel level interrupt loopback testing"] 974 | #[doc = " from userspace (ie. simulate hardware triggering)."] 975 | #[doc = ""] 976 | #[doc = " Setting of an event triggering mechanism to userspace for ACTION_TRIGGER"] 977 | #[doc = " enables the interrupt index for the device. Individual subindex interrupts"] 978 | #[doc = " can be disabled using the -1 value for DATA_EVENTFD or the index can be"] 979 | #[doc = " disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0."] 980 | #[doc = ""] 981 | #[doc = " Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while"] 982 | #[doc = " ACTION_TRIGGER specifies kernel->user signaling."] 983 | #[repr(C)] 984 | #[derive(Debug, Default)] 985 | pub struct vfio_irq_set { 986 | pub argsz: __u32, 987 | pub flags: __u32, 988 | pub index: __u32, 989 | pub start: __u32, 990 | pub count: __u32, 991 | pub data: __IncompleteArrayField<__u8>, 992 | } 993 | #[test] 994 | fn bindgen_test_layout_vfio_irq_set() { 995 | assert_eq!( 996 | ::std::mem::size_of::(), 997 | 20usize, 998 | concat!("Size of: ", stringify!(vfio_irq_set)) 999 | ); 1000 | assert_eq!( 1001 | ::std::mem::align_of::(), 1002 | 4usize, 1003 | concat!("Alignment of ", stringify!(vfio_irq_set)) 1004 | ); 1005 | } 1006 | pub const VFIO_PCI_BAR0_REGION_INDEX: _bindgen_ty_1 = 0; 1007 | pub const VFIO_PCI_BAR1_REGION_INDEX: _bindgen_ty_1 = 1; 1008 | pub const VFIO_PCI_BAR2_REGION_INDEX: _bindgen_ty_1 = 2; 1009 | pub const VFIO_PCI_BAR3_REGION_INDEX: _bindgen_ty_1 = 3; 1010 | pub const VFIO_PCI_BAR4_REGION_INDEX: _bindgen_ty_1 = 4; 1011 | pub const VFIO_PCI_BAR5_REGION_INDEX: _bindgen_ty_1 = 5; 1012 | pub const VFIO_PCI_ROM_REGION_INDEX: _bindgen_ty_1 = 6; 1013 | pub const VFIO_PCI_CONFIG_REGION_INDEX: _bindgen_ty_1 = 7; 1014 | pub const VFIO_PCI_VGA_REGION_INDEX: _bindgen_ty_1 = 8; 1015 | pub const VFIO_PCI_NUM_REGIONS: _bindgen_ty_1 = 9; 1016 | pub type _bindgen_ty_1 = u32; 1017 | pub const VFIO_PCI_INTX_IRQ_INDEX: _bindgen_ty_2 = 0; 1018 | pub const VFIO_PCI_MSI_IRQ_INDEX: _bindgen_ty_2 = 1; 1019 | pub const VFIO_PCI_MSIX_IRQ_INDEX: _bindgen_ty_2 = 2; 1020 | pub const VFIO_PCI_ERR_IRQ_INDEX: _bindgen_ty_2 = 3; 1021 | pub const VFIO_PCI_REQ_IRQ_INDEX: _bindgen_ty_2 = 4; 1022 | pub const VFIO_PCI_NUM_IRQS: _bindgen_ty_2 = 5; 1023 | pub type _bindgen_ty_2 = u32; 1024 | pub const VFIO_CCW_CONFIG_REGION_INDEX: _bindgen_ty_3 = 0; 1025 | pub const VFIO_CCW_NUM_REGIONS: _bindgen_ty_3 = 1; 1026 | pub type _bindgen_ty_3 = u32; 1027 | pub const VFIO_CCW_IO_IRQ_INDEX: _bindgen_ty_4 = 0; 1028 | pub const VFIO_CCW_NUM_IRQS: _bindgen_ty_4 = 1; 1029 | pub type _bindgen_ty_4 = u32; 1030 | #[doc = " VFIO_DEVICE_GET_PCI_HOT_RESET_INFO - _IORW(VFIO_TYPE, VFIO_BASE + 12,"] 1031 | #[doc = "\t\t\t\t\t struct vfio_pci_hot_reset_info)"] 1032 | #[doc = ""] 1033 | #[doc = " Return: 0 on success, -errno on failure:"] 1034 | #[doc = "\t-enospc = insufficient buffer, -enodev = unsupported for device."] 1035 | #[repr(C)] 1036 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1037 | pub struct vfio_pci_dependent_device { 1038 | pub group_id: __u32, 1039 | pub segment: __u16, 1040 | pub bus: __u8, 1041 | pub devfn: __u8, 1042 | } 1043 | #[test] 1044 | fn bindgen_test_layout_vfio_pci_dependent_device() { 1045 | assert_eq!( 1046 | ::std::mem::size_of::(), 1047 | 8usize, 1048 | concat!("Size of: ", stringify!(vfio_pci_dependent_device)) 1049 | ); 1050 | assert_eq!( 1051 | ::std::mem::align_of::(), 1052 | 4usize, 1053 | concat!("Alignment of ", stringify!(vfio_pci_dependent_device)) 1054 | ); 1055 | assert_eq!( 1056 | unsafe { 1057 | &(*(::std::ptr::null::())).group_id as *const _ as usize 1058 | }, 1059 | 0usize, 1060 | concat!( 1061 | "Offset of field: ", 1062 | stringify!(vfio_pci_dependent_device), 1063 | "::", 1064 | stringify!(group_id) 1065 | ) 1066 | ); 1067 | assert_eq!( 1068 | unsafe { 1069 | &(*(::std::ptr::null::())).segment as *const _ as usize 1070 | }, 1071 | 4usize, 1072 | concat!( 1073 | "Offset of field: ", 1074 | stringify!(vfio_pci_dependent_device), 1075 | "::", 1076 | stringify!(segment) 1077 | ) 1078 | ); 1079 | assert_eq!( 1080 | unsafe { &(*(::std::ptr::null::())).bus as *const _ as usize }, 1081 | 6usize, 1082 | concat!( 1083 | "Offset of field: ", 1084 | stringify!(vfio_pci_dependent_device), 1085 | "::", 1086 | stringify!(bus) 1087 | ) 1088 | ); 1089 | assert_eq!( 1090 | unsafe { &(*(::std::ptr::null::())).devfn as *const _ as usize }, 1091 | 7usize, 1092 | concat!( 1093 | "Offset of field: ", 1094 | stringify!(vfio_pci_dependent_device), 1095 | "::", 1096 | stringify!(devfn) 1097 | ) 1098 | ); 1099 | } 1100 | #[repr(C)] 1101 | #[derive(Debug, Default)] 1102 | pub struct vfio_pci_hot_reset_info { 1103 | pub argsz: __u32, 1104 | pub flags: __u32, 1105 | pub count: __u32, 1106 | pub devices: __IncompleteArrayField, 1107 | } 1108 | #[test] 1109 | fn bindgen_test_layout_vfio_pci_hot_reset_info() { 1110 | assert_eq!( 1111 | ::std::mem::size_of::(), 1112 | 12usize, 1113 | concat!("Size of: ", stringify!(vfio_pci_hot_reset_info)) 1114 | ); 1115 | assert_eq!( 1116 | ::std::mem::align_of::(), 1117 | 4usize, 1118 | concat!("Alignment of ", stringify!(vfio_pci_hot_reset_info)) 1119 | ); 1120 | } 1121 | #[doc = " VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13,"] 1122 | #[doc = "\t\t\t\t struct vfio_pci_hot_reset)"] 1123 | #[doc = ""] 1124 | #[doc = " Return: 0 on success, -errno on failure."] 1125 | #[repr(C)] 1126 | #[derive(Debug, Default)] 1127 | pub struct vfio_pci_hot_reset { 1128 | pub argsz: __u32, 1129 | pub flags: __u32, 1130 | pub count: __u32, 1131 | pub group_fds: __IncompleteArrayField<__s32>, 1132 | } 1133 | #[test] 1134 | fn bindgen_test_layout_vfio_pci_hot_reset() { 1135 | assert_eq!( 1136 | ::std::mem::size_of::(), 1137 | 12usize, 1138 | concat!("Size of: ", stringify!(vfio_pci_hot_reset)) 1139 | ); 1140 | assert_eq!( 1141 | ::std::mem::align_of::(), 1142 | 4usize, 1143 | concat!("Alignment of ", stringify!(vfio_pci_hot_reset)) 1144 | ); 1145 | } 1146 | #[doc = " VFIO_DEVICE_QUERY_GFX_PLANE - _IOW(VFIO_TYPE, VFIO_BASE + 14,"] 1147 | #[doc = " struct vfio_device_query_gfx_plane)"] 1148 | #[doc = ""] 1149 | #[doc = " Set the drm_plane_type and flags, then retrieve the gfx plane info."] 1150 | #[doc = ""] 1151 | #[doc = " flags supported:"] 1152 | #[doc = " - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_DMABUF are set"] 1153 | #[doc = " to ask if the mdev supports dma-buf. 0 on support, -EINVAL on no"] 1154 | #[doc = " support for dma-buf."] 1155 | #[doc = " - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_REGION are set"] 1156 | #[doc = " to ask if the mdev supports region. 0 on support, -EINVAL on no"] 1157 | #[doc = " support for region."] 1158 | #[doc = " - VFIO_GFX_PLANE_TYPE_DMABUF or VFIO_GFX_PLANE_TYPE_REGION is set"] 1159 | #[doc = " with each call to query the plane info."] 1160 | #[doc = " - Others are invalid and return -EINVAL."] 1161 | #[doc = ""] 1162 | #[doc = " Note:"] 1163 | #[doc = " 1. Plane could be disabled by guest. In that case, success will be"] 1164 | #[doc = " returned with zero-initialized drm_format, size, width and height"] 1165 | #[doc = " fields."] 1166 | #[doc = " 2. x_hot/y_hot is set to 0xFFFFFFFF if no hotspot information available"] 1167 | #[doc = ""] 1168 | #[doc = " Return: 0 on success, -errno on other failure."] 1169 | #[repr(C)] 1170 | #[derive(Copy, Clone)] 1171 | pub struct vfio_device_gfx_plane_info { 1172 | pub argsz: __u32, 1173 | pub flags: __u32, 1174 | pub drm_plane_type: __u32, 1175 | pub drm_format: __u32, 1176 | pub drm_format_mod: __u64, 1177 | pub width: __u32, 1178 | pub height: __u32, 1179 | pub stride: __u32, 1180 | pub size: __u32, 1181 | pub x_pos: __u32, 1182 | pub y_pos: __u32, 1183 | pub x_hot: __u32, 1184 | pub y_hot: __u32, 1185 | pub __bindgen_anon_1: vfio_device_gfx_plane_info__bindgen_ty_1, 1186 | } 1187 | #[repr(C)] 1188 | #[derive(Copy, Clone)] 1189 | pub union vfio_device_gfx_plane_info__bindgen_ty_1 { 1190 | pub region_index: __u32, 1191 | pub dmabuf_id: __u32, 1192 | _bindgen_union_align: u32, 1193 | } 1194 | #[test] 1195 | fn bindgen_test_layout_vfio_device_gfx_plane_info__bindgen_ty_1() { 1196 | assert_eq!( 1197 | ::std::mem::size_of::(), 1198 | 4usize, 1199 | concat!( 1200 | "Size of: ", 1201 | stringify!(vfio_device_gfx_plane_info__bindgen_ty_1) 1202 | ) 1203 | ); 1204 | assert_eq!( 1205 | ::std::mem::align_of::(), 1206 | 4usize, 1207 | concat!( 1208 | "Alignment of ", 1209 | stringify!(vfio_device_gfx_plane_info__bindgen_ty_1) 1210 | ) 1211 | ); 1212 | assert_eq!( 1213 | unsafe { 1214 | &(*(::std::ptr::null::())).region_index 1215 | as *const _ as usize 1216 | }, 1217 | 0usize, 1218 | concat!( 1219 | "Offset of field: ", 1220 | stringify!(vfio_device_gfx_plane_info__bindgen_ty_1), 1221 | "::", 1222 | stringify!(region_index) 1223 | ) 1224 | ); 1225 | assert_eq!( 1226 | unsafe { 1227 | &(*(::std::ptr::null::())).dmabuf_id 1228 | as *const _ as usize 1229 | }, 1230 | 0usize, 1231 | concat!( 1232 | "Offset of field: ", 1233 | stringify!(vfio_device_gfx_plane_info__bindgen_ty_1), 1234 | "::", 1235 | stringify!(dmabuf_id) 1236 | ) 1237 | ); 1238 | } 1239 | impl Default for vfio_device_gfx_plane_info__bindgen_ty_1 { 1240 | fn default() -> Self { 1241 | unsafe { ::std::mem::zeroed() } 1242 | } 1243 | } 1244 | #[test] 1245 | fn bindgen_test_layout_vfio_device_gfx_plane_info() { 1246 | assert_eq!( 1247 | ::std::mem::size_of::(), 1248 | 64usize, 1249 | concat!("Size of: ", stringify!(vfio_device_gfx_plane_info)) 1250 | ); 1251 | assert_eq!( 1252 | ::std::mem::align_of::(), 1253 | 8usize, 1254 | concat!("Alignment of ", stringify!(vfio_device_gfx_plane_info)) 1255 | ); 1256 | assert_eq!( 1257 | unsafe { 1258 | &(*(::std::ptr::null::())).argsz as *const _ as usize 1259 | }, 1260 | 0usize, 1261 | concat!( 1262 | "Offset of field: ", 1263 | stringify!(vfio_device_gfx_plane_info), 1264 | "::", 1265 | stringify!(argsz) 1266 | ) 1267 | ); 1268 | assert_eq!( 1269 | unsafe { 1270 | &(*(::std::ptr::null::())).flags as *const _ as usize 1271 | }, 1272 | 4usize, 1273 | concat!( 1274 | "Offset of field: ", 1275 | stringify!(vfio_device_gfx_plane_info), 1276 | "::", 1277 | stringify!(flags) 1278 | ) 1279 | ); 1280 | assert_eq!( 1281 | unsafe { 1282 | &(*(::std::ptr::null::())).drm_plane_type as *const _ 1283 | as usize 1284 | }, 1285 | 8usize, 1286 | concat!( 1287 | "Offset of field: ", 1288 | stringify!(vfio_device_gfx_plane_info), 1289 | "::", 1290 | stringify!(drm_plane_type) 1291 | ) 1292 | ); 1293 | assert_eq!( 1294 | unsafe { 1295 | &(*(::std::ptr::null::())).drm_format as *const _ as usize 1296 | }, 1297 | 12usize, 1298 | concat!( 1299 | "Offset of field: ", 1300 | stringify!(vfio_device_gfx_plane_info), 1301 | "::", 1302 | stringify!(drm_format) 1303 | ) 1304 | ); 1305 | assert_eq!( 1306 | unsafe { 1307 | &(*(::std::ptr::null::())).drm_format_mod as *const _ 1308 | as usize 1309 | }, 1310 | 16usize, 1311 | concat!( 1312 | "Offset of field: ", 1313 | stringify!(vfio_device_gfx_plane_info), 1314 | "::", 1315 | stringify!(drm_format_mod) 1316 | ) 1317 | ); 1318 | assert_eq!( 1319 | unsafe { 1320 | &(*(::std::ptr::null::())).width as *const _ as usize 1321 | }, 1322 | 24usize, 1323 | concat!( 1324 | "Offset of field: ", 1325 | stringify!(vfio_device_gfx_plane_info), 1326 | "::", 1327 | stringify!(width) 1328 | ) 1329 | ); 1330 | assert_eq!( 1331 | unsafe { 1332 | &(*(::std::ptr::null::())).height as *const _ as usize 1333 | }, 1334 | 28usize, 1335 | concat!( 1336 | "Offset of field: ", 1337 | stringify!(vfio_device_gfx_plane_info), 1338 | "::", 1339 | stringify!(height) 1340 | ) 1341 | ); 1342 | assert_eq!( 1343 | unsafe { 1344 | &(*(::std::ptr::null::())).stride as *const _ as usize 1345 | }, 1346 | 32usize, 1347 | concat!( 1348 | "Offset of field: ", 1349 | stringify!(vfio_device_gfx_plane_info), 1350 | "::", 1351 | stringify!(stride) 1352 | ) 1353 | ); 1354 | assert_eq!( 1355 | unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, 1356 | 36usize, 1357 | concat!( 1358 | "Offset of field: ", 1359 | stringify!(vfio_device_gfx_plane_info), 1360 | "::", 1361 | stringify!(size) 1362 | ) 1363 | ); 1364 | assert_eq!( 1365 | unsafe { 1366 | &(*(::std::ptr::null::())).x_pos as *const _ as usize 1367 | }, 1368 | 40usize, 1369 | concat!( 1370 | "Offset of field: ", 1371 | stringify!(vfio_device_gfx_plane_info), 1372 | "::", 1373 | stringify!(x_pos) 1374 | ) 1375 | ); 1376 | assert_eq!( 1377 | unsafe { 1378 | &(*(::std::ptr::null::())).y_pos as *const _ as usize 1379 | }, 1380 | 44usize, 1381 | concat!( 1382 | "Offset of field: ", 1383 | stringify!(vfio_device_gfx_plane_info), 1384 | "::", 1385 | stringify!(y_pos) 1386 | ) 1387 | ); 1388 | assert_eq!( 1389 | unsafe { 1390 | &(*(::std::ptr::null::())).x_hot as *const _ as usize 1391 | }, 1392 | 48usize, 1393 | concat!( 1394 | "Offset of field: ", 1395 | stringify!(vfio_device_gfx_plane_info), 1396 | "::", 1397 | stringify!(x_hot) 1398 | ) 1399 | ); 1400 | assert_eq!( 1401 | unsafe { 1402 | &(*(::std::ptr::null::())).y_hot as *const _ as usize 1403 | }, 1404 | 52usize, 1405 | concat!( 1406 | "Offset of field: ", 1407 | stringify!(vfio_device_gfx_plane_info), 1408 | "::", 1409 | stringify!(y_hot) 1410 | ) 1411 | ); 1412 | } 1413 | impl Default for vfio_device_gfx_plane_info { 1414 | fn default() -> Self { 1415 | unsafe { ::std::mem::zeroed() } 1416 | } 1417 | } 1418 | #[doc = " VFIO_DEVICE_IOEVENTFD - _IOW(VFIO_TYPE, VFIO_BASE + 16,"] 1419 | #[doc = " struct vfio_device_ioeventfd)"] 1420 | #[doc = ""] 1421 | #[doc = " Perform a write to the device at the specified device fd offset, with"] 1422 | #[doc = " the specified data and width when the provided eventfd is triggered."] 1423 | #[doc = " vfio bus drivers may not support this for all regions, for all widths,"] 1424 | #[doc = " or at all. vfio-pci currently only enables support for BAR regions,"] 1425 | #[doc = " excluding the MSI-X vector table."] 1426 | #[doc = ""] 1427 | #[doc = " Return: 0 on success, -errno on failure."] 1428 | #[repr(C)] 1429 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1430 | pub struct vfio_device_ioeventfd { 1431 | pub argsz: __u32, 1432 | pub flags: __u32, 1433 | pub offset: __u64, 1434 | pub data: __u64, 1435 | pub fd: __s32, 1436 | } 1437 | #[test] 1438 | fn bindgen_test_layout_vfio_device_ioeventfd() { 1439 | assert_eq!( 1440 | ::std::mem::size_of::(), 1441 | 32usize, 1442 | concat!("Size of: ", stringify!(vfio_device_ioeventfd)) 1443 | ); 1444 | assert_eq!( 1445 | ::std::mem::align_of::(), 1446 | 8usize, 1447 | concat!("Alignment of ", stringify!(vfio_device_ioeventfd)) 1448 | ); 1449 | assert_eq!( 1450 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 1451 | 0usize, 1452 | concat!( 1453 | "Offset of field: ", 1454 | stringify!(vfio_device_ioeventfd), 1455 | "::", 1456 | stringify!(argsz) 1457 | ) 1458 | ); 1459 | assert_eq!( 1460 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 1461 | 4usize, 1462 | concat!( 1463 | "Offset of field: ", 1464 | stringify!(vfio_device_ioeventfd), 1465 | "::", 1466 | stringify!(flags) 1467 | ) 1468 | ); 1469 | assert_eq!( 1470 | unsafe { &(*(::std::ptr::null::())).offset as *const _ as usize }, 1471 | 8usize, 1472 | concat!( 1473 | "Offset of field: ", 1474 | stringify!(vfio_device_ioeventfd), 1475 | "::", 1476 | stringify!(offset) 1477 | ) 1478 | ); 1479 | assert_eq!( 1480 | unsafe { &(*(::std::ptr::null::())).data as *const _ as usize }, 1481 | 16usize, 1482 | concat!( 1483 | "Offset of field: ", 1484 | stringify!(vfio_device_ioeventfd), 1485 | "::", 1486 | stringify!(data) 1487 | ) 1488 | ); 1489 | assert_eq!( 1490 | unsafe { &(*(::std::ptr::null::())).fd as *const _ as usize }, 1491 | 24usize, 1492 | concat!( 1493 | "Offset of field: ", 1494 | stringify!(vfio_device_ioeventfd), 1495 | "::", 1496 | stringify!(fd) 1497 | ) 1498 | ); 1499 | } 1500 | #[doc = " VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info)"] 1501 | #[doc = ""] 1502 | #[doc = " Retrieve information about the IOMMU object. Fills in provided"] 1503 | #[doc = " struct vfio_iommu_info. Caller sets argsz."] 1504 | #[doc = ""] 1505 | #[doc = " XXX Should we do these by CHECK_EXTENSION too?"] 1506 | #[repr(C)] 1507 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1508 | pub struct vfio_iommu_type1_info { 1509 | pub argsz: __u32, 1510 | pub flags: __u32, 1511 | pub iova_pgsizes: __u64, 1512 | } 1513 | #[test] 1514 | fn bindgen_test_layout_vfio_iommu_type1_info() { 1515 | assert_eq!( 1516 | ::std::mem::size_of::(), 1517 | 16usize, 1518 | concat!("Size of: ", stringify!(vfio_iommu_type1_info)) 1519 | ); 1520 | assert_eq!( 1521 | ::std::mem::align_of::(), 1522 | 8usize, 1523 | concat!("Alignment of ", stringify!(vfio_iommu_type1_info)) 1524 | ); 1525 | assert_eq!( 1526 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 1527 | 0usize, 1528 | concat!( 1529 | "Offset of field: ", 1530 | stringify!(vfio_iommu_type1_info), 1531 | "::", 1532 | stringify!(argsz) 1533 | ) 1534 | ); 1535 | assert_eq!( 1536 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 1537 | 4usize, 1538 | concat!( 1539 | "Offset of field: ", 1540 | stringify!(vfio_iommu_type1_info), 1541 | "::", 1542 | stringify!(flags) 1543 | ) 1544 | ); 1545 | assert_eq!( 1546 | unsafe { 1547 | &(*(::std::ptr::null::())).iova_pgsizes as *const _ as usize 1548 | }, 1549 | 8usize, 1550 | concat!( 1551 | "Offset of field: ", 1552 | stringify!(vfio_iommu_type1_info), 1553 | "::", 1554 | stringify!(iova_pgsizes) 1555 | ) 1556 | ); 1557 | } 1558 | #[doc = " VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map)"] 1559 | #[doc = ""] 1560 | #[doc = " Map process virtual addresses to IO virtual addresses using the"] 1561 | #[doc = " provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required."] 1562 | #[repr(C)] 1563 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1564 | pub struct vfio_iommu_type1_dma_map { 1565 | pub argsz: __u32, 1566 | pub flags: __u32, 1567 | pub vaddr: __u64, 1568 | pub iova: __u64, 1569 | pub size: __u64, 1570 | } 1571 | #[test] 1572 | fn bindgen_test_layout_vfio_iommu_type1_dma_map() { 1573 | assert_eq!( 1574 | ::std::mem::size_of::(), 1575 | 32usize, 1576 | concat!("Size of: ", stringify!(vfio_iommu_type1_dma_map)) 1577 | ); 1578 | assert_eq!( 1579 | ::std::mem::align_of::(), 1580 | 8usize, 1581 | concat!("Alignment of ", stringify!(vfio_iommu_type1_dma_map)) 1582 | ); 1583 | assert_eq!( 1584 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 1585 | 0usize, 1586 | concat!( 1587 | "Offset of field: ", 1588 | stringify!(vfio_iommu_type1_dma_map), 1589 | "::", 1590 | stringify!(argsz) 1591 | ) 1592 | ); 1593 | assert_eq!( 1594 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 1595 | 4usize, 1596 | concat!( 1597 | "Offset of field: ", 1598 | stringify!(vfio_iommu_type1_dma_map), 1599 | "::", 1600 | stringify!(flags) 1601 | ) 1602 | ); 1603 | assert_eq!( 1604 | unsafe { &(*(::std::ptr::null::())).vaddr as *const _ as usize }, 1605 | 8usize, 1606 | concat!( 1607 | "Offset of field: ", 1608 | stringify!(vfio_iommu_type1_dma_map), 1609 | "::", 1610 | stringify!(vaddr) 1611 | ) 1612 | ); 1613 | assert_eq!( 1614 | unsafe { &(*(::std::ptr::null::())).iova as *const _ as usize }, 1615 | 16usize, 1616 | concat!( 1617 | "Offset of field: ", 1618 | stringify!(vfio_iommu_type1_dma_map), 1619 | "::", 1620 | stringify!(iova) 1621 | ) 1622 | ); 1623 | assert_eq!( 1624 | unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, 1625 | 24usize, 1626 | concat!( 1627 | "Offset of field: ", 1628 | stringify!(vfio_iommu_type1_dma_map), 1629 | "::", 1630 | stringify!(size) 1631 | ) 1632 | ); 1633 | } 1634 | #[doc = " VFIO_IOMMU_UNMAP_DMA - _IOWR(VFIO_TYPE, VFIO_BASE + 14,"] 1635 | #[doc = "\t\t\t\t\t\t\tstruct vfio_dma_unmap)"] 1636 | #[doc = ""] 1637 | #[doc = " Unmap IO virtual addresses using the provided struct vfio_dma_unmap."] 1638 | #[doc = " Caller sets argsz. The actual unmapped size is returned in the size"] 1639 | #[doc = " field. No guarantee is made to the user that arbitrary unmaps of iova"] 1640 | #[doc = " or size different from those used in the original mapping call will"] 1641 | #[doc = " succeed."] 1642 | #[repr(C)] 1643 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1644 | pub struct vfio_iommu_type1_dma_unmap { 1645 | pub argsz: __u32, 1646 | pub flags: __u32, 1647 | pub iova: __u64, 1648 | pub size: __u64, 1649 | } 1650 | #[test] 1651 | fn bindgen_test_layout_vfio_iommu_type1_dma_unmap() { 1652 | assert_eq!( 1653 | ::std::mem::size_of::(), 1654 | 24usize, 1655 | concat!("Size of: ", stringify!(vfio_iommu_type1_dma_unmap)) 1656 | ); 1657 | assert_eq!( 1658 | ::std::mem::align_of::(), 1659 | 8usize, 1660 | concat!("Alignment of ", stringify!(vfio_iommu_type1_dma_unmap)) 1661 | ); 1662 | assert_eq!( 1663 | unsafe { 1664 | &(*(::std::ptr::null::())).argsz as *const _ as usize 1665 | }, 1666 | 0usize, 1667 | concat!( 1668 | "Offset of field: ", 1669 | stringify!(vfio_iommu_type1_dma_unmap), 1670 | "::", 1671 | stringify!(argsz) 1672 | ) 1673 | ); 1674 | assert_eq!( 1675 | unsafe { 1676 | &(*(::std::ptr::null::())).flags as *const _ as usize 1677 | }, 1678 | 4usize, 1679 | concat!( 1680 | "Offset of field: ", 1681 | stringify!(vfio_iommu_type1_dma_unmap), 1682 | "::", 1683 | stringify!(flags) 1684 | ) 1685 | ); 1686 | assert_eq!( 1687 | unsafe { &(*(::std::ptr::null::())).iova as *const _ as usize }, 1688 | 8usize, 1689 | concat!( 1690 | "Offset of field: ", 1691 | stringify!(vfio_iommu_type1_dma_unmap), 1692 | "::", 1693 | stringify!(iova) 1694 | ) 1695 | ); 1696 | assert_eq!( 1697 | unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, 1698 | 16usize, 1699 | concat!( 1700 | "Offset of field: ", 1701 | stringify!(vfio_iommu_type1_dma_unmap), 1702 | "::", 1703 | stringify!(size) 1704 | ) 1705 | ); 1706 | } 1707 | #[repr(C)] 1708 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1709 | pub struct vfio_iommu_spapr_tce_ddw_info { 1710 | pub pgsizes: __u64, 1711 | pub max_dynamic_windows_supported: __u32, 1712 | pub levels: __u32, 1713 | } 1714 | #[test] 1715 | fn bindgen_test_layout_vfio_iommu_spapr_tce_ddw_info() { 1716 | assert_eq!( 1717 | ::std::mem::size_of::(), 1718 | 16usize, 1719 | concat!("Size of: ", stringify!(vfio_iommu_spapr_tce_ddw_info)) 1720 | ); 1721 | assert_eq!( 1722 | ::std::mem::align_of::(), 1723 | 8usize, 1724 | concat!("Alignment of ", stringify!(vfio_iommu_spapr_tce_ddw_info)) 1725 | ); 1726 | assert_eq!( 1727 | unsafe { 1728 | &(*(::std::ptr::null::())).pgsizes as *const _ as usize 1729 | }, 1730 | 0usize, 1731 | concat!( 1732 | "Offset of field: ", 1733 | stringify!(vfio_iommu_spapr_tce_ddw_info), 1734 | "::", 1735 | stringify!(pgsizes) 1736 | ) 1737 | ); 1738 | assert_eq!( 1739 | unsafe { 1740 | &(*(::std::ptr::null::())).max_dynamic_windows_supported 1741 | as *const _ as usize 1742 | }, 1743 | 8usize, 1744 | concat!( 1745 | "Offset of field: ", 1746 | stringify!(vfio_iommu_spapr_tce_ddw_info), 1747 | "::", 1748 | stringify!(max_dynamic_windows_supported) 1749 | ) 1750 | ); 1751 | assert_eq!( 1752 | unsafe { 1753 | &(*(::std::ptr::null::())).levels as *const _ as usize 1754 | }, 1755 | 12usize, 1756 | concat!( 1757 | "Offset of field: ", 1758 | stringify!(vfio_iommu_spapr_tce_ddw_info), 1759 | "::", 1760 | stringify!(levels) 1761 | ) 1762 | ); 1763 | } 1764 | #[repr(C)] 1765 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1766 | pub struct vfio_iommu_spapr_tce_info { 1767 | pub argsz: __u32, 1768 | pub flags: __u32, 1769 | pub dma32_window_start: __u32, 1770 | pub dma32_window_size: __u32, 1771 | pub ddw: vfio_iommu_spapr_tce_ddw_info, 1772 | } 1773 | #[test] 1774 | fn bindgen_test_layout_vfio_iommu_spapr_tce_info() { 1775 | assert_eq!( 1776 | ::std::mem::size_of::(), 1777 | 32usize, 1778 | concat!("Size of: ", stringify!(vfio_iommu_spapr_tce_info)) 1779 | ); 1780 | assert_eq!( 1781 | ::std::mem::align_of::(), 1782 | 8usize, 1783 | concat!("Alignment of ", stringify!(vfio_iommu_spapr_tce_info)) 1784 | ); 1785 | assert_eq!( 1786 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 1787 | 0usize, 1788 | concat!( 1789 | "Offset of field: ", 1790 | stringify!(vfio_iommu_spapr_tce_info), 1791 | "::", 1792 | stringify!(argsz) 1793 | ) 1794 | ); 1795 | assert_eq!( 1796 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 1797 | 4usize, 1798 | concat!( 1799 | "Offset of field: ", 1800 | stringify!(vfio_iommu_spapr_tce_info), 1801 | "::", 1802 | stringify!(flags) 1803 | ) 1804 | ); 1805 | assert_eq!( 1806 | unsafe { 1807 | &(*(::std::ptr::null::())).dma32_window_start as *const _ 1808 | as usize 1809 | }, 1810 | 8usize, 1811 | concat!( 1812 | "Offset of field: ", 1813 | stringify!(vfio_iommu_spapr_tce_info), 1814 | "::", 1815 | stringify!(dma32_window_start) 1816 | ) 1817 | ); 1818 | assert_eq!( 1819 | unsafe { 1820 | &(*(::std::ptr::null::())).dma32_window_size as *const _ 1821 | as usize 1822 | }, 1823 | 12usize, 1824 | concat!( 1825 | "Offset of field: ", 1826 | stringify!(vfio_iommu_spapr_tce_info), 1827 | "::", 1828 | stringify!(dma32_window_size) 1829 | ) 1830 | ); 1831 | assert_eq!( 1832 | unsafe { &(*(::std::ptr::null::())).ddw as *const _ as usize }, 1833 | 16usize, 1834 | concat!( 1835 | "Offset of field: ", 1836 | stringify!(vfio_iommu_spapr_tce_info), 1837 | "::", 1838 | stringify!(ddw) 1839 | ) 1840 | ); 1841 | } 1842 | #[repr(C)] 1843 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 1844 | pub struct vfio_eeh_pe_err { 1845 | pub type_: __u32, 1846 | pub func: __u32, 1847 | pub addr: __u64, 1848 | pub mask: __u64, 1849 | } 1850 | #[test] 1851 | fn bindgen_test_layout_vfio_eeh_pe_err() { 1852 | assert_eq!( 1853 | ::std::mem::size_of::(), 1854 | 24usize, 1855 | concat!("Size of: ", stringify!(vfio_eeh_pe_err)) 1856 | ); 1857 | assert_eq!( 1858 | ::std::mem::align_of::(), 1859 | 8usize, 1860 | concat!("Alignment of ", stringify!(vfio_eeh_pe_err)) 1861 | ); 1862 | assert_eq!( 1863 | unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, 1864 | 0usize, 1865 | concat!( 1866 | "Offset of field: ", 1867 | stringify!(vfio_eeh_pe_err), 1868 | "::", 1869 | stringify!(type_) 1870 | ) 1871 | ); 1872 | assert_eq!( 1873 | unsafe { &(*(::std::ptr::null::())).func as *const _ as usize }, 1874 | 4usize, 1875 | concat!( 1876 | "Offset of field: ", 1877 | stringify!(vfio_eeh_pe_err), 1878 | "::", 1879 | stringify!(func) 1880 | ) 1881 | ); 1882 | assert_eq!( 1883 | unsafe { &(*(::std::ptr::null::())).addr as *const _ as usize }, 1884 | 8usize, 1885 | concat!( 1886 | "Offset of field: ", 1887 | stringify!(vfio_eeh_pe_err), 1888 | "::", 1889 | stringify!(addr) 1890 | ) 1891 | ); 1892 | assert_eq!( 1893 | unsafe { &(*(::std::ptr::null::())).mask as *const _ as usize }, 1894 | 16usize, 1895 | concat!( 1896 | "Offset of field: ", 1897 | stringify!(vfio_eeh_pe_err), 1898 | "::", 1899 | stringify!(mask) 1900 | ) 1901 | ); 1902 | } 1903 | #[repr(C)] 1904 | #[derive(Copy, Clone)] 1905 | pub struct vfio_eeh_pe_op { 1906 | pub argsz: __u32, 1907 | pub flags: __u32, 1908 | pub op: __u32, 1909 | pub __bindgen_anon_1: vfio_eeh_pe_op__bindgen_ty_1, 1910 | } 1911 | #[repr(C)] 1912 | #[derive(Copy, Clone)] 1913 | pub union vfio_eeh_pe_op__bindgen_ty_1 { 1914 | pub err: vfio_eeh_pe_err, 1915 | _bindgen_union_align: [u64; 3usize], 1916 | } 1917 | #[test] 1918 | fn bindgen_test_layout_vfio_eeh_pe_op__bindgen_ty_1() { 1919 | assert_eq!( 1920 | ::std::mem::size_of::(), 1921 | 24usize, 1922 | concat!("Size of: ", stringify!(vfio_eeh_pe_op__bindgen_ty_1)) 1923 | ); 1924 | assert_eq!( 1925 | ::std::mem::align_of::(), 1926 | 8usize, 1927 | concat!("Alignment of ", stringify!(vfio_eeh_pe_op__bindgen_ty_1)) 1928 | ); 1929 | assert_eq!( 1930 | unsafe { 1931 | &(*(::std::ptr::null::())).err as *const _ as usize 1932 | }, 1933 | 0usize, 1934 | concat!( 1935 | "Offset of field: ", 1936 | stringify!(vfio_eeh_pe_op__bindgen_ty_1), 1937 | "::", 1938 | stringify!(err) 1939 | ) 1940 | ); 1941 | } 1942 | impl Default for vfio_eeh_pe_op__bindgen_ty_1 { 1943 | fn default() -> Self { 1944 | unsafe { ::std::mem::zeroed() } 1945 | } 1946 | } 1947 | #[test] 1948 | fn bindgen_test_layout_vfio_eeh_pe_op() { 1949 | assert_eq!( 1950 | ::std::mem::size_of::(), 1951 | 40usize, 1952 | concat!("Size of: ", stringify!(vfio_eeh_pe_op)) 1953 | ); 1954 | assert_eq!( 1955 | ::std::mem::align_of::(), 1956 | 8usize, 1957 | concat!("Alignment of ", stringify!(vfio_eeh_pe_op)) 1958 | ); 1959 | assert_eq!( 1960 | unsafe { &(*(::std::ptr::null::())).argsz as *const _ as usize }, 1961 | 0usize, 1962 | concat!( 1963 | "Offset of field: ", 1964 | stringify!(vfio_eeh_pe_op), 1965 | "::", 1966 | stringify!(argsz) 1967 | ) 1968 | ); 1969 | assert_eq!( 1970 | unsafe { &(*(::std::ptr::null::())).flags as *const _ as usize }, 1971 | 4usize, 1972 | concat!( 1973 | "Offset of field: ", 1974 | stringify!(vfio_eeh_pe_op), 1975 | "::", 1976 | stringify!(flags) 1977 | ) 1978 | ); 1979 | assert_eq!( 1980 | unsafe { &(*(::std::ptr::null::())).op as *const _ as usize }, 1981 | 8usize, 1982 | concat!( 1983 | "Offset of field: ", 1984 | stringify!(vfio_eeh_pe_op), 1985 | "::", 1986 | stringify!(op) 1987 | ) 1988 | ); 1989 | } 1990 | impl Default for vfio_eeh_pe_op { 1991 | fn default() -> Self { 1992 | unsafe { ::std::mem::zeroed() } 1993 | } 1994 | } 1995 | #[doc = " VFIO_IOMMU_SPAPR_REGISTER_MEMORY - _IOW(VFIO_TYPE, VFIO_BASE + 17, struct vfio_iommu_spapr_register_memory)"] 1996 | #[doc = ""] 1997 | #[doc = " Registers user space memory where DMA is allowed. It pins"] 1998 | #[doc = " user pages and does the locked memory accounting so"] 1999 | #[doc = " subsequent VFIO_IOMMU_MAP_DMA/VFIO_IOMMU_UNMAP_DMA calls"] 2000 | #[doc = " get faster."] 2001 | #[repr(C)] 2002 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 2003 | pub struct vfio_iommu_spapr_register_memory { 2004 | pub argsz: __u32, 2005 | pub flags: __u32, 2006 | pub vaddr: __u64, 2007 | pub size: __u64, 2008 | } 2009 | #[test] 2010 | fn bindgen_test_layout_vfio_iommu_spapr_register_memory() { 2011 | assert_eq!( 2012 | ::std::mem::size_of::(), 2013 | 24usize, 2014 | concat!("Size of: ", stringify!(vfio_iommu_spapr_register_memory)) 2015 | ); 2016 | assert_eq!( 2017 | ::std::mem::align_of::(), 2018 | 8usize, 2019 | concat!( 2020 | "Alignment of ", 2021 | stringify!(vfio_iommu_spapr_register_memory) 2022 | ) 2023 | ); 2024 | assert_eq!( 2025 | unsafe { 2026 | &(*(::std::ptr::null::())).argsz as *const _ as usize 2027 | }, 2028 | 0usize, 2029 | concat!( 2030 | "Offset of field: ", 2031 | stringify!(vfio_iommu_spapr_register_memory), 2032 | "::", 2033 | stringify!(argsz) 2034 | ) 2035 | ); 2036 | assert_eq!( 2037 | unsafe { 2038 | &(*(::std::ptr::null::())).flags as *const _ as usize 2039 | }, 2040 | 4usize, 2041 | concat!( 2042 | "Offset of field: ", 2043 | stringify!(vfio_iommu_spapr_register_memory), 2044 | "::", 2045 | stringify!(flags) 2046 | ) 2047 | ); 2048 | assert_eq!( 2049 | unsafe { 2050 | &(*(::std::ptr::null::())).vaddr as *const _ as usize 2051 | }, 2052 | 8usize, 2053 | concat!( 2054 | "Offset of field: ", 2055 | stringify!(vfio_iommu_spapr_register_memory), 2056 | "::", 2057 | stringify!(vaddr) 2058 | ) 2059 | ); 2060 | assert_eq!( 2061 | unsafe { 2062 | &(*(::std::ptr::null::())).size as *const _ as usize 2063 | }, 2064 | 16usize, 2065 | concat!( 2066 | "Offset of field: ", 2067 | stringify!(vfio_iommu_spapr_register_memory), 2068 | "::", 2069 | stringify!(size) 2070 | ) 2071 | ); 2072 | } 2073 | #[doc = " VFIO_IOMMU_SPAPR_TCE_CREATE - _IOWR(VFIO_TYPE, VFIO_BASE + 19, struct vfio_iommu_spapr_tce_create)"] 2074 | #[doc = ""] 2075 | #[doc = " Creates an additional TCE table and programs it (sets a new DMA window)"] 2076 | #[doc = " to every IOMMU group in the container. It receives page shift, window"] 2077 | #[doc = " size and number of levels in the TCE table being created."] 2078 | #[doc = ""] 2079 | #[doc = " It allocates and returns an offset on a PCI bus of the new DMA window."] 2080 | #[repr(C)] 2081 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 2082 | pub struct vfio_iommu_spapr_tce_create { 2083 | pub argsz: __u32, 2084 | pub flags: __u32, 2085 | pub page_shift: __u32, 2086 | pub __resv1: __u32, 2087 | pub window_size: __u64, 2088 | pub levels: __u32, 2089 | pub __resv2: __u32, 2090 | pub start_addr: __u64, 2091 | } 2092 | #[test] 2093 | fn bindgen_test_layout_vfio_iommu_spapr_tce_create() { 2094 | assert_eq!( 2095 | ::std::mem::size_of::(), 2096 | 40usize, 2097 | concat!("Size of: ", stringify!(vfio_iommu_spapr_tce_create)) 2098 | ); 2099 | assert_eq!( 2100 | ::std::mem::align_of::(), 2101 | 8usize, 2102 | concat!("Alignment of ", stringify!(vfio_iommu_spapr_tce_create)) 2103 | ); 2104 | assert_eq!( 2105 | unsafe { 2106 | &(*(::std::ptr::null::())).argsz as *const _ as usize 2107 | }, 2108 | 0usize, 2109 | concat!( 2110 | "Offset of field: ", 2111 | stringify!(vfio_iommu_spapr_tce_create), 2112 | "::", 2113 | stringify!(argsz) 2114 | ) 2115 | ); 2116 | assert_eq!( 2117 | unsafe { 2118 | &(*(::std::ptr::null::())).flags as *const _ as usize 2119 | }, 2120 | 4usize, 2121 | concat!( 2122 | "Offset of field: ", 2123 | stringify!(vfio_iommu_spapr_tce_create), 2124 | "::", 2125 | stringify!(flags) 2126 | ) 2127 | ); 2128 | assert_eq!( 2129 | unsafe { 2130 | &(*(::std::ptr::null::())).page_shift as *const _ as usize 2131 | }, 2132 | 8usize, 2133 | concat!( 2134 | "Offset of field: ", 2135 | stringify!(vfio_iommu_spapr_tce_create), 2136 | "::", 2137 | stringify!(page_shift) 2138 | ) 2139 | ); 2140 | assert_eq!( 2141 | unsafe { 2142 | &(*(::std::ptr::null::())).__resv1 as *const _ as usize 2143 | }, 2144 | 12usize, 2145 | concat!( 2146 | "Offset of field: ", 2147 | stringify!(vfio_iommu_spapr_tce_create), 2148 | "::", 2149 | stringify!(__resv1) 2150 | ) 2151 | ); 2152 | assert_eq!( 2153 | unsafe { 2154 | &(*(::std::ptr::null::())).window_size as *const _ as usize 2155 | }, 2156 | 16usize, 2157 | concat!( 2158 | "Offset of field: ", 2159 | stringify!(vfio_iommu_spapr_tce_create), 2160 | "::", 2161 | stringify!(window_size) 2162 | ) 2163 | ); 2164 | assert_eq!( 2165 | unsafe { 2166 | &(*(::std::ptr::null::())).levels as *const _ as usize 2167 | }, 2168 | 24usize, 2169 | concat!( 2170 | "Offset of field: ", 2171 | stringify!(vfio_iommu_spapr_tce_create), 2172 | "::", 2173 | stringify!(levels) 2174 | ) 2175 | ); 2176 | assert_eq!( 2177 | unsafe { 2178 | &(*(::std::ptr::null::())).__resv2 as *const _ as usize 2179 | }, 2180 | 28usize, 2181 | concat!( 2182 | "Offset of field: ", 2183 | stringify!(vfio_iommu_spapr_tce_create), 2184 | "::", 2185 | stringify!(__resv2) 2186 | ) 2187 | ); 2188 | assert_eq!( 2189 | unsafe { 2190 | &(*(::std::ptr::null::())).start_addr as *const _ as usize 2191 | }, 2192 | 32usize, 2193 | concat!( 2194 | "Offset of field: ", 2195 | stringify!(vfio_iommu_spapr_tce_create), 2196 | "::", 2197 | stringify!(start_addr) 2198 | ) 2199 | ); 2200 | } 2201 | #[doc = " VFIO_IOMMU_SPAPR_TCE_REMOVE - _IOW(VFIO_TYPE, VFIO_BASE + 20, struct vfio_iommu_spapr_tce_remove)"] 2202 | #[doc = ""] 2203 | #[doc = " Unprograms a TCE table from all groups in the container and destroys it."] 2204 | #[doc = " It receives a PCI bus offset as a window id."] 2205 | #[repr(C)] 2206 | #[derive(Debug, Default, Copy, Clone, PartialEq)] 2207 | pub struct vfio_iommu_spapr_tce_remove { 2208 | pub argsz: __u32, 2209 | pub flags: __u32, 2210 | pub start_addr: __u64, 2211 | } 2212 | #[test] 2213 | fn bindgen_test_layout_vfio_iommu_spapr_tce_remove() { 2214 | assert_eq!( 2215 | ::std::mem::size_of::(), 2216 | 16usize, 2217 | concat!("Size of: ", stringify!(vfio_iommu_spapr_tce_remove)) 2218 | ); 2219 | assert_eq!( 2220 | ::std::mem::align_of::(), 2221 | 8usize, 2222 | concat!("Alignment of ", stringify!(vfio_iommu_spapr_tce_remove)) 2223 | ); 2224 | assert_eq!( 2225 | unsafe { 2226 | &(*(::std::ptr::null::())).argsz as *const _ as usize 2227 | }, 2228 | 0usize, 2229 | concat!( 2230 | "Offset of field: ", 2231 | stringify!(vfio_iommu_spapr_tce_remove), 2232 | "::", 2233 | stringify!(argsz) 2234 | ) 2235 | ); 2236 | assert_eq!( 2237 | unsafe { 2238 | &(*(::std::ptr::null::())).flags as *const _ as usize 2239 | }, 2240 | 4usize, 2241 | concat!( 2242 | "Offset of field: ", 2243 | stringify!(vfio_iommu_spapr_tce_remove), 2244 | "::", 2245 | stringify!(flags) 2246 | ) 2247 | ); 2248 | assert_eq!( 2249 | unsafe { 2250 | &(*(::std::ptr::null::())).start_addr as *const _ as usize 2251 | }, 2252 | 8usize, 2253 | concat!( 2254 | "Offset of field: ", 2255 | stringify!(vfio_iommu_spapr_tce_remove), 2256 | "::", 2257 | stringify!(start_addr) 2258 | ) 2259 | ); 2260 | } 2261 | -------------------------------------------------------------------------------- /crates/vfio-bindings/src/fam_wrappers.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 4 | // 5 | 6 | use crate::bindings::vfio::*; 7 | use vmm_sys_util::fam::{FamStruct, FamStructWrapper}; 8 | 9 | const MSIX_MAX_VECTORS: usize = 2048; 10 | 11 | // Implement the FamStruct trait vfio_irq_set. 12 | generate_fam_struct_impl!(vfio_irq_set, u8, data, u32, count, MSIX_MAX_VECTORS); 13 | 14 | /// Wrapper over the `vfio_irq_set` structure. 15 | /// 16 | /// The `vfio_irq_set` structure contains a flexible array member. For details check the 17 | /// [VFIO userspace API](https://github.com/torvalds/linux/blob/master/include/uapi/linux/vfio.h) 18 | /// documentation on `vfio_irq_set`. To provide safe access to the array 19 | /// elements, this type is implemented using 20 | /// [FamStructWrapper](../vmm_sys_util/fam/struct.FamStructWrapper.html). 21 | pub type IrqSet = FamStructWrapper; 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | extern crate byteorder; 26 | 27 | use super::*; 28 | use byteorder::{ByteOrder, LittleEndian}; 29 | use std::mem; 30 | 31 | fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { 32 | let rounded_size = (size_in_bytes + mem::size_of::() - 1) / mem::size_of::(); 33 | let mut v = Vec::with_capacity(rounded_size); 34 | for _ in 0..rounded_size { 35 | v.push(T::default()) 36 | } 37 | v 38 | } 39 | 40 | fn vec_with_array_field(count: usize) -> Vec { 41 | let element_space = count * mem::size_of::(); 42 | let vec_size_bytes = mem::size_of::() + element_space; 43 | vec_with_size_in_bytes(vec_size_bytes) 44 | } 45 | 46 | // Opinionated PartialEq implementation for vfio_irq_set. 47 | impl PartialEq for vfio_irq_set { 48 | fn eq(&self, other: &Self) -> bool { 49 | if self.argsz != other.argsz 50 | || self.flags != other.flags 51 | || self.index != other.index 52 | || self.start != other.start 53 | || self.count != other.count 54 | { 55 | return false; 56 | } 57 | true 58 | } 59 | } 60 | 61 | #[test] 62 | fn irqset_fam_test() { 63 | let event_fds: Vec = vec![0, 1, 2, 3, 4, 5]; 64 | 65 | // Build a FAM wrapper for this vfio_irq_set. 66 | let mut irq_set_wrapper = IrqSet::new(event_fds.len() * mem::size_of::()).unwrap(); 67 | let mut irq_set_fam = irq_set_wrapper.as_mut_fam_struct(); 68 | 69 | let fds_fam = irq_set_fam.as_mut_slice(); 70 | for (index, event_fd) in event_fds.iter().enumerate() { 71 | let fds_offset = index * mem::size_of::(); 72 | let fd = &mut fds_fam[fds_offset..fds_offset + mem::size_of::()]; 73 | LittleEndian::write_u32(fd, *event_fd); 74 | } 75 | 76 | irq_set_fam.argsz = mem::size_of::() as u32 77 | + (event_fds.len() * mem::size_of::()) as u32; 78 | irq_set_fam.flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 79 | irq_set_fam.index = 1; 80 | irq_set_fam.start = 0; 81 | irq_set_fam.count = event_fds.len() as u32; 82 | 83 | // Build the same vfio_irq_set structure with the vec_with_array routines 84 | let mut irq_set_vec = vec_with_array_field::(event_fds.len()); 85 | irq_set_vec[0].argsz = mem::size_of::() as u32 86 | + (event_fds.len() * mem::size_of::()) as u32; 87 | irq_set_vec[0].flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 88 | irq_set_vec[0].index = 1; 89 | irq_set_vec[0].start = 0; 90 | irq_set_vec[0].count = event_fds.len() as u32; 91 | 92 | let fds_vec = unsafe { 93 | irq_set_vec[0] 94 | .data 95 | .as_mut_slice(event_fds.len() * mem::size_of::()) 96 | }; 97 | for (index, event_fd) in event_fds.iter().enumerate() { 98 | let fds_offset = index * mem::size_of::(); 99 | let fd = &mut fds_vec[fds_offset..fds_offset + mem::size_of::()]; 100 | LittleEndian::write_u32(fd, *event_fd); 101 | } 102 | 103 | // Both sets should be identical. 104 | assert_eq!( 105 | irq_set_vec 106 | .iter() 107 | .zip(irq_set_wrapper.into_raw().iter()) 108 | .filter(|&(a, b)| a == b) 109 | .count(), 110 | irq_set_vec.len() 111 | ); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /crates/vfio-bindings/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Intel Corporation. All Rights Reserved. 2 | // SPDX-License-Identifier: (BSD-3-Clause OR Apache-2.0) 3 | 4 | #[cfg(feature = "fam-wrappers")] 5 | #[macro_use] 6 | extern crate vmm_sys_util; 7 | 8 | #[cfg(feature = "fam-wrappers")] 9 | mod fam_wrappers; 10 | 11 | #[cfg(feature = "vfio-v5_0_0")] 12 | mod bindings_v5_0_0; 13 | 14 | // Default to latest version if no version is specified by using the features. 15 | #[cfg(not(feature = "vfio-v5_0_0"))] 16 | mod bindings_v5_0_0; 17 | 18 | pub mod bindings { 19 | #[cfg(feature = "vfio-v5_0_0")] 20 | pub use super::bindings_v5_0_0::*; 21 | 22 | #[cfg(not(feature = "vfio-v5_0_0"))] 23 | pub use super::bindings_v5_0_0::*; 24 | 25 | #[cfg(feature = "fam-wrappers")] 26 | pub use super::fam_wrappers::*; 27 | } 28 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfio-ioctls" 3 | version = "0.1.0" 4 | authors = ["The Cloud Hypervisor Authors", "Liu Jiang "] 5 | license = "Apache-2.0 OR BSD-3-Clause" 6 | description = "Safe wrappers over VFIO ioctls" 7 | repository = "https://github.com/rust-vmm/vfio" 8 | readme = "README.md" 9 | edition = "2018" 10 | keywords = ["vfio"] 11 | 12 | [features] 13 | default = ["kvm"] 14 | kvm = ["kvm-ioctls", "kvm-bindings"] 15 | mshv = ["mshv-ioctls", "mshv-bindings"] 16 | 17 | [dependencies] 18 | byteorder = ">=1.2.1" 19 | libc = ">=0.2.39" 20 | log = "0.4" 21 | kvm-bindings = { version = "~0", optional = true } 22 | kvm-ioctls = { version = "~0", optional = true } 23 | thiserror = ">=1.0" 24 | vfio-bindings = "~0" 25 | vm-memory = { version = ">=0.6", features = ["backend-mmap"] } 26 | vmm-sys-util = ">=0.8.0" 27 | mshv-bindings = { git = "https://github.com/rust-vmm/mshv", branch = "main", features = ["with-serde", "fam-wrappers"], optional = true } 28 | mshv-ioctls = { git = "https://github.com/rust-vmm/mshv", branch = "main", optional = true } 29 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/LICENSE-BSD: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright 2019 Intel Corporation. All Rights Reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/README.md: -------------------------------------------------------------------------------- 1 | # vfio-ioctls 2 | 3 | ## Design 4 | 5 | The [VFIO driver framework](https://www.kernel.org/doc/Documentation/vfio.txt) 6 | provides unified APIs for direct device access. It is an IOMMU/device-agnostic framework for 7 | exposing direct device access to user space in a secure, IOMMU-protected environment. 8 | This framework is used for multiple devices, such as GPUs, network adapters, and compute 9 | accelerators. With direct device access, virtual machines or user space applications have 10 | direct access to the physical device. 11 | 12 | The VFIO framework is originally developed on Linux system, and later Microsoft HyperVisor 13 | technology provides a compatible implementation. Therefore the VFIO framework is supported 14 | by both Linux and Microsoft HyperVisor. 15 | 16 | The `vfio-ioctls` crate is a safe wrapper over the VFIO APIs. It provides three classes of structs: 17 | - `VfioContainer`: a safe wrapper over a VFIO container object, and acts a container object 18 | to associate `VfioDevice` objects with IOMMU domains. 19 | - `VfioDevice`: a wrapper over a VFIO device object, provide methods to access the underlying 20 | hardware device. 21 | - `VfioIrq/VfioRegion`: describes capabilities/resources about a `VfioDevice` object. 22 | 23 | ## Usage 24 | 25 | The `vfio-ioctls` crate may be used to support following usage scenarios: 26 | - Direct device assignment to virtual machine based on Linux KVM, with default features. 27 | - Direct device assignment to virtual machine based on Microsoft HyperVisor, with `--no-default-features --features=mshv`. 28 | - User mode device drivers, with `--no-default-features`. 29 | 30 | First, add the following to your Cargo.toml: 31 | ```toml 32 | vfio-ioctls = "0.1" 33 | ``` 34 | Next, add this to your crate root: 35 | 36 | ```rust 37 | extern crate vfio_ioctls; 38 | ``` 39 | 40 | By default vfio-ioctls has the `kvm` feature enabled. You may turn off the default features by 41 | `default-features = false`. To enable feature `mshv`, 42 | ```toml 43 | vfio-ioctls = { version = "0.1", default-features = false, features = ["mshv"]} 44 | ``` 45 | 46 | 47 | ## Examples 48 | 49 | To create VFIO device object for user mode drivers, 50 | 51 | ```rust 52 | use std::sync::Arc; 53 | use vfio_ioctls::{VfioContainer, VfioDevice}; 54 | 55 | fn create_vfio_device() { 56 | // TODO: change to your device's path 57 | let device_path = "/sys/bus/pci/devices/00:03.0"; 58 | let vfio_container = Arc::new(VfioContainer::new(()).unwrap()); 59 | let vfio_dev = VfioDevice::new(&Path::new(device_path), vfio_container.clone()).unwrap(); 60 | let irqs = vfio_dev.max_interrupts(); 61 | 62 | assert!(irqs > 0); 63 | } 64 | ``` 65 | 66 | ## License 67 | 68 | This code is licensed under Apache-2.0 or BSD-3-Clause. 69 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/src/fam.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 4 | 5 | // This is a private version of vmm-sys-util::FamStruct. As it works smoothly, we keep it for 6 | // simplicity. 7 | 8 | use std::mem::size_of; 9 | 10 | /// Returns a `Vec` with a size in bytes at least as large as `size_in_bytes`. 11 | fn vec_with_size_in_bytes(size_in_bytes: usize) -> Vec { 12 | let rounded_size = (size_in_bytes + size_of::() - 1) / size_of::(); 13 | let mut v = Vec::with_capacity(rounded_size); 14 | for _ in 0..rounded_size { 15 | v.push(T::default()) 16 | } 17 | v 18 | } 19 | 20 | /// The VFIO API has several structs that resembles the following `Foo` structure: 21 | /// 22 | /// ``` 23 | /// struct ControlMessageHeader { 24 | /// r#type: u8, 25 | /// length: u8, 26 | /// } 27 | /// 28 | /// #[repr(C)] 29 | /// pub struct __IncompleteArrayField(::std::marker::PhantomData); 30 | /// #[repr(C)] 31 | /// struct Foo { 32 | /// some_data: ControlMessageHeader, 33 | /// entries: __IncompleteArrayField, 34 | /// } 35 | /// ``` 36 | /// 37 | /// In order to allocate such a structure, `size_of::()` would be too small because it would not 38 | /// include any space for `entries`. To make the allocation large enough while still being aligned 39 | /// for `Foo`, a `Vec` is created. Only the first element of `Vec` would actually be used 40 | /// as a `Foo`. The remaining memory in the `Vec` is for `entries`, which must be contiguous 41 | /// with `Foo`. This function is used to make the `Vec` with enough space for `count` entries. 42 | pub(crate) fn vec_with_array_field(count: usize) -> Vec { 43 | let element_space = match count.checked_mul(size_of::()) { 44 | None => panic!("allocating too large buffer with vec_with_array_field"), 45 | Some(v) => v, 46 | }; 47 | let vec_size_bytes = match element_space.checked_add(size_of::()) { 48 | None => panic!("allocating too large buffer with vec_with_array_field"), 49 | Some(v) => v, 50 | }; 51 | 52 | vec_with_size_in_bytes(vec_size_bytes) 53 | } 54 | 55 | #[cfg(test)] 56 | mod tests { 57 | use super::*; 58 | 59 | #[derive(Default)] 60 | #[allow(dead_code)] 61 | struct Header { 62 | ty: u32, 63 | len: u32, 64 | } 65 | 66 | #[allow(dead_code)] 67 | struct Field { 68 | f1: u64, 69 | f2: u64, 70 | } 71 | 72 | #[test] 73 | fn test_vec_with_array_field() { 74 | let v1 = vec_with_array_field::(1); 75 | assert_eq!(v1.len(), 3); 76 | 77 | let v2 = vec_with_array_field::(0); 78 | assert_eq!(v2.len(), 1); 79 | 80 | let v3 = vec_with_array_field::(5); 81 | assert_eq!(v3.len(), 11); 82 | } 83 | 84 | #[test] 85 | #[should_panic] 86 | fn test_vec_with_array_field_overflow() { 87 | let _ = vec_with_array_field::(usize::MAX); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 4 | 5 | //! [Virtual Function I/O (VFIO) API](https://www.kernel.org/doc/Documentation/vfio.txt) 6 | //! 7 | //! Many modern system now provide DMA and interrupt remapping facilities to help ensure I/O 8 | //! devices behave within the boundaries they've been allotted. This includes x86 hardware with 9 | //! AMD-Vi and Intel VT-d, POWER systems with Partitionable Endpoints (PEs) and embedded PowerPC 10 | //! systems such as Freescale PAMU. The VFIO driver is an IOMMU/device agnostic framework for 11 | //! exposing direct device access to userspace, in a secure, IOMMU protected environment. 12 | //! In other words, the VFIO framework allows safe, non-privileged, userspace drivers. 13 | //! 14 | //! Why do we want that? Virtual machines often make use of direct device access ("device 15 | //! assignment") when configured for the highest possible I/O performance. From a device and host 16 | //! perspective, this simply turns the VM into a userspace driver, with the benefits of 17 | //! significantly reduced latency, higher bandwidth, and direct use of bare-metal device drivers. 18 | //! 19 | //! Devices are the main target of any I/O driver. Devices typically create a programming 20 | //! interface made up of I/O access, interrupts, and DMA. Without going into the details of each 21 | //! of these, DMA is by far the most critical aspect for maintaining a secure environment as 22 | //! allowing a device read-write access to system memory imposes the greatest risk to the overall 23 | //! system integrity. 24 | //! 25 | //! To help mitigate this risk, many modern IOMMUs now incorporate isolation properties into what 26 | //! was, in many cases, an interface only meant for translation (ie. solving the addressing 27 | //! problems of devices with limited address spaces). With this, devices can now be isolated 28 | //! from each other and from arbitrary memory access, thus allowing things like secure direct 29 | //! assignment of devices into virtual machines. 30 | //! 31 | //! While for the most part an IOMMU may have device level granularity, any system is susceptible 32 | //! to reduced granularity. The IOMMU API therefore supports a notion of IOMMU groups. A group is 33 | //! a set of devices which is isolatable from all other devices in the system. Groups are therefore 34 | //! the unit of ownership used by VFIO. 35 | //! 36 | //! While the group is the minimum granularity that must be used to ensure secure user access, it's 37 | //! not necessarily the preferred granularity. In IOMMUs which make use of page tables, it may be 38 | //! possible to share a set of page tables between different groups, reducing the overhead both to 39 | //! the platform (reduced TLB thrashing, reduced duplicate page tables), and to the user 40 | //! (programming only a single set of translations). For this reason, VFIO makes use of a container 41 | //! class, which may hold one or more groups. A container is created by simply opening the 42 | //! /dev/vfio/vfio character device. 43 | //! 44 | //! This crate is a safe wrapper around the Linux kernel's VFIO interfaces, which offering safe 45 | //! wrappers for: 46 | //! - [VFIO Container](struct.VfioContainer.html) using the `VfioContainer` structure 47 | //! - [VFIO Device](struct.VfioDevice.html) using the `VfioDevice` structure 48 | //! 49 | //! # Platform support 50 | //! 51 | //! - x86_64 52 | //! 53 | //! **NOTE:** The list of available ioctls is not exhaustive. 54 | 55 | #![deny(missing_docs)] 56 | 57 | #[macro_use] 58 | extern crate vmm_sys_util; 59 | 60 | use std::io; 61 | use thiserror::Error; 62 | use vmm_sys_util::errno::Error as SysError; 63 | 64 | mod fam; 65 | mod vfio_device; 66 | mod vfio_ioctls; 67 | 68 | pub use vfio_device::{ 69 | VfioContainer, VfioDevice, VfioGroup, VfioIrq, VfioRegion, VfioRegionInfoCap, 70 | VfioRegionInfoCapNvlink2Lnkspd, VfioRegionInfoCapNvlink2Ssatgt, VfioRegionInfoCapSparseMmap, 71 | VfioRegionInfoCapType, VfioRegionSparseMmapArea, 72 | }; 73 | 74 | /// Error codes for VFIO operations. 75 | #[derive(Debug, Error)] 76 | #[allow(missing_docs)] 77 | pub enum VfioError { 78 | #[error("failed to open /dev/vfio/vfio container: {0}")] 79 | OpenContainer(#[source] io::Error), 80 | #[error("failed to open /dev/vfio/{1} group: {0}")] 81 | OpenGroup(#[source] io::Error, String), 82 | #[error("failed to get Group Status")] 83 | GetGroupStatus, 84 | #[error("group is not viable")] 85 | GroupViable, 86 | #[error("vfio API version doesn't match with VFIO_API_VERSION defined in vfio-bindings")] 87 | VfioApiVersion, 88 | #[error("failed to check VFIO extension")] 89 | VfioExtension, 90 | #[error("invalid VFIO type")] 91 | VfioInvalidType, 92 | #[error("container doesn't support VfioType1V2 IOMMU driver type")] 93 | VfioType1V2, 94 | #[error("failed to add vfio group into vfio container")] 95 | GroupSetContainer, 96 | #[error("failed to unset vfio container")] 97 | UnsetContainer, 98 | #[error("failed to set container's IOMMU driver type as VfioType1V2")] 99 | ContainerSetIOMMU, 100 | #[error("failed to get vfio device fd")] 101 | GroupGetDeviceFD, 102 | #[error("failed to set vfio device's attribute: {0}")] 103 | SetDeviceAttr(#[source] SysError), 104 | #[error("failed to get vfio device's info or info doesn't match")] 105 | VfioDeviceGetInfo, 106 | #[error("failed to get vfio device's region info: {0}")] 107 | VfioDeviceGetRegionInfo(#[source] SysError), 108 | #[error("invalid file path")] 109 | InvalidPath, 110 | #[error("failed to add guest memory map into iommu table")] 111 | IommuDmaMap, 112 | #[error("failed to remove guest memory map from iommu table")] 113 | IommuDmaUnmap, 114 | #[error("failed to get vfio device irq info")] 115 | VfioDeviceGetIrqInfo, 116 | #[error("failed to set vfio device irq")] 117 | VfioDeviceSetIrq, 118 | #[error("failed to enable vfio device irq")] 119 | VfioDeviceEnableIrq, 120 | #[error("failed to disable vfio device irq")] 121 | VfioDeviceDisableIrq, 122 | #[error("failed to unmask vfio device irq")] 123 | VfioDeviceUnmaskIrq, 124 | #[error("failed to trigger vfio device irq")] 125 | VfioDeviceTriggerIrq, 126 | } 127 | 128 | /// Specialized version of `Result` for VFIO subsystem. 129 | pub type Result = std::result::Result; 130 | 131 | #[cfg(test)] 132 | mod tests { 133 | use super::*; 134 | use std::error::Error; 135 | 136 | #[test] 137 | fn test_vfio_error_fmt() { 138 | let e = VfioError::GetGroupStatus; 139 | let e2 = VfioError::OpenContainer(std::io::Error::from(std::io::ErrorKind::Other)); 140 | let str = format!("{}", e); 141 | 142 | assert_eq!(&str, "failed to get Group Status"); 143 | assert!(e2.source().is_some()); 144 | assert!(e.source().is_none()); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/src/vfio_device.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 Intel Corporation 2 | // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 5 | 6 | use std::collections::HashMap; 7 | use std::ffi::CString; 8 | use std::fs::{File, OpenOptions}; 9 | use std::mem::{self, ManuallyDrop}; 10 | use std::os::unix::io::{AsRawFd, RawFd}; 11 | use std::os::unix::prelude::FileExt; 12 | use std::path::Path; 13 | #[cfg(not(test))] 14 | use std::path::PathBuf; 15 | use std::sync::{Arc, Mutex}; 16 | 17 | use byteorder::{ByteOrder, LittleEndian}; 18 | use log::{debug, error, warn}; 19 | use vfio_bindings::bindings::vfio::*; 20 | use vm_memory::{Address, GuestMemory, GuestMemoryRegion, MemoryRegionAddress}; 21 | use vmm_sys_util::eventfd::EventFd; 22 | 23 | use crate::fam::vec_with_array_field; 24 | use crate::vfio_ioctls::*; 25 | use crate::{Result, VfioError}; 26 | 27 | #[repr(C)] 28 | #[derive(Debug, Default)] 29 | // A VFIO region structure with an incomplete array for region 30 | // capabilities information. 31 | // 32 | // When the VFIO_DEVICE_GET_REGION_INFO ioctl returns with 33 | // VFIO_REGION_INFO_FLAG_CAPS flag set, it also provides the size of the region 34 | // capabilities information. This is a kernel hint for us to fetch this 35 | // information by calling the same ioctl, but with the argument size set to 36 | // the region plus the capabilities information array length. The kernel will 37 | // then fill our vfio_region_info_with_cap structure with both the region info 38 | // and its capabilities. 39 | pub struct vfio_region_info_with_cap { 40 | pub region_info: vfio_region_info, 41 | cap_info: __IncompleteArrayField, 42 | } 43 | 44 | impl vfio_region_info_with_cap { 45 | fn from_region_info(region_info: &vfio_region_info) -> Vec { 46 | let region_info_size: u32 = mem::size_of::() as u32; 47 | let cap_len: usize = (region_info.argsz - region_info_size) as usize; 48 | 49 | let mut region_with_cap = vec_with_array_field::(cap_len); 50 | region_with_cap[0].region_info.argsz = region_info.argsz; 51 | region_with_cap[0].region_info.flags = 0; 52 | region_with_cap[0].region_info.index = region_info.index; 53 | region_with_cap[0].region_info.cap_offset = 0; 54 | region_with_cap[0].region_info.size = 0; 55 | region_with_cap[0].region_info.offset = 0; 56 | 57 | region_with_cap 58 | } 59 | } 60 | 61 | pub use self::hypervisor::VfioContainerDeviceHandle; 62 | 63 | /// A safe wrapper over a VFIO container object. 64 | /// 65 | /// A VFIO container represents an IOMMU domain, or a set of IO virtual address translation tables. 66 | /// On its own, the container provides little functionality, with all but a couple version and 67 | /// extension query interfaces locked away. The user needs to add a group into the container for 68 | /// the next level of functionality. After some groups are associated with a container, the user 69 | /// can query and set the IOMMU backend, and then build IOVA mapping to access memory. 70 | /// 71 | /// Multiple VFIO groups may be associated with the same VFIO container to share the underline 72 | /// address translation mapping tables. 73 | pub struct VfioContainer { 74 | pub(crate) container: File, 75 | #[allow(dead_code)] 76 | pub(crate) device_fd: VfioContainerDeviceHandle, 77 | pub(crate) groups: Mutex>>, 78 | } 79 | 80 | impl VfioContainer { 81 | /// Create a container wrapper object. 82 | /// 83 | /// # Arguments 84 | /// * `device_fd`: file handle of the VFIO device. 85 | pub fn new(device_fd: VfioContainerDeviceHandle) -> Result { 86 | let container = OpenOptions::new() 87 | .read(true) 88 | .write(true) 89 | .open("/dev/vfio/vfio") 90 | .map_err(VfioError::OpenContainer)?; 91 | 92 | let container = VfioContainer { 93 | container, 94 | device_fd, 95 | groups: Mutex::new(HashMap::new()), 96 | }; 97 | container.check_api_version()?; 98 | container.check_extension(VFIO_TYPE1v2_IOMMU)?; 99 | 100 | Ok(container) 101 | } 102 | 103 | fn check_api_version(&self) -> Result<()> { 104 | let version = vfio_syscall::check_api_version(self); 105 | if version as u32 != VFIO_API_VERSION { 106 | return Err(VfioError::VfioApiVersion); 107 | } 108 | Ok(()) 109 | } 110 | 111 | fn check_extension(&self, val: u32) -> Result<()> { 112 | if val != VFIO_TYPE1_IOMMU && val != VFIO_TYPE1v2_IOMMU { 113 | return Err(VfioError::VfioInvalidType); 114 | } 115 | 116 | let ret = vfio_syscall::check_extension(self, val)?; 117 | if ret != 1 { 118 | return Err(VfioError::VfioExtension); 119 | } 120 | 121 | Ok(()) 122 | } 123 | 124 | fn set_iommu(&self, val: u32) -> Result<()> { 125 | if val != VFIO_TYPE1_IOMMU && val != VFIO_TYPE1v2_IOMMU { 126 | return Err(VfioError::VfioInvalidType); 127 | } 128 | 129 | vfio_syscall::set_iommu(self, val) 130 | } 131 | 132 | fn get_group(&self, group_id: u32) -> Result> { 133 | // Safe because there's no legal way to break the lock. 134 | let mut hash = self.groups.lock().unwrap(); 135 | if let Some(entry) = hash.get(&group_id) { 136 | return Ok(entry.clone()); 137 | } 138 | 139 | let group = Arc::new(VfioGroup::new(group_id)?); 140 | 141 | // Bind the new group object to the container. 142 | vfio_syscall::set_group_container(&*group, self)?; 143 | 144 | // Initialize the IOMMU backend driver after binding the first group object. 145 | if hash.len() == 0 { 146 | if let Err(e) = self.set_iommu(VFIO_TYPE1v2_IOMMU) { 147 | let _ = vfio_syscall::unset_group_container(&*group, self); 148 | return Err(e); 149 | } 150 | } 151 | 152 | // Add the new group object to the hypervisor driver. 153 | if let Err(e) = self.device_add_group(&group) { 154 | let _ = vfio_syscall::unset_group_container(&*group, self); 155 | return Err(e); 156 | } 157 | 158 | hash.insert(group_id, group.clone()); 159 | 160 | Ok(group) 161 | } 162 | 163 | fn put_group(&self, group: Arc) { 164 | // Safe because there's no legal way to break the lock. 165 | let mut hash = self.groups.lock().unwrap(); 166 | 167 | // Clean up the group when the last user releases reference to the group, three reference 168 | // count for: 169 | // - one reference held by the last device object 170 | // - one reference cloned in VfioDevice.drop() and passed into here 171 | // - one reference held by the groups hashmap 172 | if Arc::strong_count(&group) == 3 { 173 | match self.device_del_group(&group) { 174 | Ok(_) => {} 175 | Err(e) => { 176 | error!("Could not delete VFIO group: {:?}", e); 177 | return; 178 | } 179 | } 180 | if vfio_syscall::unset_group_container(&*group, self).is_err() { 181 | error!("Could not unbind VFIO group: {:?}", group.id()); 182 | return; 183 | } 184 | hash.remove(&group.id()); 185 | } 186 | } 187 | 188 | /// Map a region of guest memory regions into the vfio container's iommu table. 189 | /// 190 | /// # Parameters 191 | /// * iova: IO virtual address to mapping the memory. 192 | /// * size: size of the memory region. 193 | /// * user_addr: host virtual address for the guest memory region to map. 194 | pub fn vfio_dma_map(&self, iova: u64, size: u64, user_addr: u64) -> Result<()> { 195 | let dma_map = vfio_iommu_type1_dma_map { 196 | argsz: mem::size_of::() as u32, 197 | flags: VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE, 198 | vaddr: user_addr, 199 | iova, 200 | size, 201 | }; 202 | 203 | vfio_syscall::map_dma(self, &dma_map) 204 | } 205 | 206 | /// Unmap a region of guest memory regions into the vfio container's iommu table. 207 | /// 208 | /// # Parameters 209 | /// * iova: IO virtual address to mapping the memory. 210 | /// * size: size of the memory region. 211 | pub fn vfio_dma_unmap(&self, iova: u64, size: u64) -> Result<()> { 212 | let mut dma_unmap = vfio_iommu_type1_dma_unmap { 213 | argsz: mem::size_of::() as u32, 214 | flags: 0, 215 | iova, 216 | size, 217 | }; 218 | 219 | vfio_syscall::unmap_dma(self, &mut dma_unmap)?; 220 | if dma_unmap.size != size { 221 | return Err(VfioError::IommuDmaUnmap); 222 | } 223 | 224 | Ok(()) 225 | } 226 | 227 | /// Add all guest memory regions into the vfio container's iommu table. 228 | /// 229 | /// # Parameters 230 | /// * mem: pinned guest memory which could be accessed by devices binding to the container. 231 | pub fn vfio_map_guest_memory(&self, mem: &M) -> Result<()> { 232 | mem.iter().try_for_each(|region| { 233 | let host_addr = region 234 | .get_host_address(MemoryRegionAddress(0)) 235 | .map_err(|_| VfioError::IommuDmaMap)?; 236 | self.vfio_dma_map( 237 | region.start_addr().raw_value(), 238 | region.len() as u64, 239 | host_addr as u64, 240 | ) 241 | }) 242 | } 243 | 244 | /// Remove all guest memory regions from the vfio container's iommu table. 245 | /// 246 | /// The vfio kernel driver and device hardware couldn't access this guest memory after 247 | /// returning from the function. 248 | /// 249 | /// # Parameters 250 | /// * mem: pinned guest memory which could be accessed by devices binding to the container. 251 | pub fn vfio_unmap_guest_memory(&self, mem: &M) -> Result<()> { 252 | mem.iter().try_for_each(|region| { 253 | self.vfio_dma_unmap(region.start_addr().raw_value(), region.len() as u64) 254 | }) 255 | } 256 | } 257 | 258 | #[cfg(all(feature = "kvm", not(test)))] 259 | // Methods to support the KVM hypervisor. 260 | // Note: a special stub implementation is used for VFIO unit tests, so following code won't covered 261 | // by unit tests, be careful when review changes. 262 | mod hypervisor { 263 | use super::*; 264 | use kvm_bindings::{ 265 | kvm_device_attr, KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_DEV_VFIO_GROUP_DEL, 266 | }; 267 | use kvm_ioctls::DeviceFd; 268 | 269 | /// Type for device file handle passed to VfioContainer::new(); 270 | pub type VfioContainerDeviceHandle = Arc; 271 | 272 | impl VfioContainer { 273 | pub(crate) fn device_add_group(&self, group: &VfioGroup) -> Result<()> { 274 | let group_fd_ptr = &group.as_raw_fd() as *const i32; 275 | let dev_attr = kvm_device_attr { 276 | flags: 0, 277 | group: KVM_DEV_VFIO_GROUP, 278 | attr: u64::from(KVM_DEV_VFIO_GROUP_ADD), 279 | addr: group_fd_ptr as u64, 280 | }; 281 | 282 | self.device_fd 283 | .set_device_attr(&dev_attr) 284 | .map_err(VfioError::SetDeviceAttr) 285 | } 286 | 287 | pub(crate) fn device_del_group(&self, group: &VfioGroup) -> Result<()> { 288 | let group_fd_ptr = &group.as_raw_fd() as *const i32; 289 | let dev_attr = kvm_device_attr { 290 | flags: 0, 291 | group: KVM_DEV_VFIO_GROUP, 292 | attr: u64::from(KVM_DEV_VFIO_GROUP_DEL), 293 | addr: group_fd_ptr as u64, 294 | }; 295 | 296 | self.device_fd 297 | .set_device_attr(&dev_attr) 298 | .map_err(VfioError::SetDeviceAttr) 299 | } 300 | } 301 | } 302 | 303 | #[cfg(all(feature = "mshv", not(feature = "kvm"), not(test)))] 304 | // Methods to support the Microsoft HyperVisor. 305 | // Note: a special stub implementation is used for VFIO unit tests, so following code won't covered 306 | // by unit tests, be careful when review changes. 307 | mod hypervisor { 308 | use super::*; 309 | use mshv_bindings::{ 310 | mshv_device_attr, MSHV_DEV_VFIO_GROUP, MSHV_DEV_VFIO_GROUP_ADD, MSHV_DEV_VFIO_GROUP_DEL, 311 | }; 312 | use mshv_ioctls::DeviceFd; 313 | 314 | /// Type for device file handle passed to VfioContainer::new(); 315 | pub type VfioContainerDeviceHandle = Arc; 316 | 317 | impl VfioContainer { 318 | pub(crate) fn device_add_group(&self, group: &VfioGroup) -> Result<()> { 319 | let group_fd_ptr = &group.as_raw_fd() as *const i32; 320 | let dev_attr = mshv_device_attr { 321 | flags: 0, 322 | group: MSHV_DEV_VFIO_GROUP, 323 | attr: u64::from(MSHV_DEV_VFIO_GROUP_ADD), 324 | addr: group_fd_ptr as u64, 325 | }; 326 | 327 | self.device_fd 328 | .set_device_attr(&dev_attr) 329 | .map_err(VfioError::SetDeviceAttr) 330 | } 331 | 332 | pub(crate) fn device_del_group(&self, group: &VfioGroup) -> Result<()> { 333 | let group_fd_ptr = &group.as_raw_fd() as *const i32; 334 | let dev_attr = mshv_device_attr { 335 | flags: 0, 336 | group: MSHV_DEV_VFIO_GROUP, 337 | attr: u64::from(MSHV_DEV_VFIO_GROUP_DEL), 338 | addr: group_fd_ptr as u64, 339 | }; 340 | 341 | self.device_fd 342 | .set_device_attr(&dev_attr) 343 | .map_err(VfioError::SetDeviceAttr) 344 | } 345 | } 346 | } 347 | 348 | #[cfg(any(test, all(not(feature = "mshv"), not(feature = "kvm"))))] 349 | // Methods to support user mode driver, which has no associated hypervisors. 350 | // This implementation also acts a stub for VFIO unit tests to avoid dependency on platform 351 | // hardware configuration. 352 | mod hypervisor { 353 | use super::*; 354 | 355 | /// Type for device file handle passed to VfioContainer::new(); 356 | pub type VfioContainerDeviceHandle = (); 357 | 358 | impl VfioContainer { 359 | pub(crate) fn device_add_group(&self, _group: &VfioGroup) -> Result<()> { 360 | Ok(()) 361 | } 362 | 363 | pub(crate) fn device_del_group(&self, _group: &VfioGroup) -> Result<()> { 364 | Ok(()) 365 | } 366 | } 367 | } 368 | 369 | impl AsRawFd for VfioContainer { 370 | fn as_raw_fd(&self) -> RawFd { 371 | self.container.as_raw_fd() 372 | } 373 | } 374 | 375 | /// A safe wrapper over a VFIO group object. 376 | /// 377 | /// The Linux VFIO frameworks supports multiple devices per group, and multiple groups per 378 | /// container. But current implementation assumes there's only one device per group to simplify 379 | /// implementation. With such an assumption, the `VfioGroup` becomes an internal implementation 380 | /// details. 381 | pub struct VfioGroup { 382 | pub(crate) id: u32, 383 | pub(crate) group: File, 384 | } 385 | 386 | impl VfioGroup { 387 | #[cfg(not(test))] 388 | fn open_group_file(id: u32) -> Result { 389 | let group_path = Path::new("/dev/vfio").join(id.to_string()); 390 | OpenOptions::new() 391 | .read(true) 392 | .write(true) 393 | .open(&group_path) 394 | .map_err(|e| VfioError::OpenGroup(e, id.to_string())) 395 | } 396 | 397 | /// Create a new VfioGroup object. 398 | /// 399 | /// # Parameters 400 | /// * `id`: ID(index) of the VFIO group file. 401 | fn new(id: u32) -> Result { 402 | let group = Self::open_group_file(id)?; 403 | let mut group_status = vfio_group_status { 404 | argsz: mem::size_of::() as u32, 405 | flags: 0, 406 | }; 407 | vfio_syscall::get_group_status(&group, &mut group_status)?; 408 | if group_status.flags != VFIO_GROUP_FLAGS_VIABLE { 409 | return Err(VfioError::GroupViable); 410 | } 411 | 412 | Ok(VfioGroup { id, group }) 413 | } 414 | 415 | fn id(&self) -> u32 { 416 | self.id 417 | } 418 | 419 | fn get_device(&self, name: &Path) -> Result { 420 | let uuid_osstr = name.file_name().ok_or(VfioError::InvalidPath)?; 421 | let uuid_str = uuid_osstr.to_str().ok_or(VfioError::InvalidPath)?; 422 | let path: CString = CString::new(uuid_str.as_bytes()).expect("CString::new() failed"); 423 | let device = vfio_syscall::get_group_device_fd(self, &path)?; 424 | 425 | let mut dev_info = vfio_device_info { 426 | argsz: mem::size_of::() as u32, 427 | flags: 0, 428 | num_regions: 0, 429 | num_irqs: 0, 430 | }; 431 | vfio_syscall::get_device_info(&device, &mut dev_info)?; 432 | if (dev_info.flags & VFIO_DEVICE_FLAGS_PCI) == 0 433 | || dev_info.num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1 434 | || dev_info.num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1 435 | { 436 | return Err(VfioError::VfioDeviceGetInfo); 437 | } 438 | 439 | Ok(VfioDeviceInfo::new(device, &dev_info)) 440 | } 441 | } 442 | 443 | impl AsRawFd for VfioGroup { 444 | fn as_raw_fd(&self) -> RawFd { 445 | self.group.as_raw_fd() 446 | } 447 | } 448 | 449 | /// Represent one area of the sparse mmap 450 | #[derive(Copy, Clone, Debug, PartialEq)] 451 | pub struct VfioRegionSparseMmapArea { 452 | /// Offset of mmap'able area within region 453 | pub offset: u64, 454 | /// Size of mmap'able area 455 | pub size: u64, 456 | } 457 | 458 | /// List of sparse mmap areas 459 | #[derive(Clone, Debug, PartialEq)] 460 | pub struct VfioRegionInfoCapSparseMmap { 461 | /// List of areas 462 | pub areas: Vec, 463 | } 464 | 465 | /// Represent a specific device by providing type and subtype 466 | #[derive(Copy, Clone, Debug, PartialEq)] 467 | pub struct VfioRegionInfoCapType { 468 | /// Device type 469 | pub type_: u32, 470 | /// Device subtype 471 | pub subtype: u32, 472 | } 473 | 474 | /// Carry NVLink SSA TGT information 475 | #[derive(Copy, Clone, Debug, PartialEq)] 476 | pub struct VfioRegionInfoCapNvlink2Ssatgt { 477 | /// TGT value 478 | pub tgt: u64, 479 | } 480 | 481 | /// Carry NVLink link speed information 482 | #[derive(Copy, Clone, Debug, PartialEq)] 483 | pub struct VfioRegionInfoCapNvlink2Lnkspd { 484 | /// Link speed value 485 | pub link_speed: u32, 486 | } 487 | 488 | /// List of capabilities that can be related to a region. 489 | #[derive(Clone, Debug, PartialEq)] 490 | pub enum VfioRegionInfoCap { 491 | /// Sparse memory mapping type 492 | SparseMmap(VfioRegionInfoCapSparseMmap), 493 | /// Capability holding type and subtype 494 | Type(VfioRegionInfoCapType), 495 | /// Indicate if the region is mmap'able with the presence of MSI-X region 496 | MsixMappable, 497 | /// NVLink SSA TGT 498 | Nvlink2Ssatgt(VfioRegionInfoCapNvlink2Ssatgt), 499 | /// NVLink Link Speed 500 | Nvlink2Lnkspd(VfioRegionInfoCapNvlink2Lnkspd), 501 | } 502 | 503 | /// Information about VFIO MMIO region. 504 | #[derive(Clone, Debug)] 505 | pub struct VfioRegion { 506 | pub(crate) flags: u32, 507 | pub(crate) size: u64, 508 | pub(crate) offset: u64, 509 | pub(crate) caps: Vec, 510 | } 511 | 512 | /// Information about VFIO interrupts. 513 | #[derive(Copy, Clone, Debug, PartialEq)] 514 | pub struct VfioIrq { 515 | /// Flags for irq. 516 | pub flags: u32, 517 | /// Staring index. 518 | pub index: u32, 519 | /// Number interrupts. 520 | pub count: u32, 521 | } 522 | 523 | pub(crate) struct VfioDeviceInfo { 524 | device: File, 525 | flags: u32, 526 | num_regions: u32, 527 | num_irqs: u32, 528 | } 529 | 530 | impl VfioDeviceInfo { 531 | fn new(device: File, dev_info: &vfio_device_info) -> Self { 532 | VfioDeviceInfo { 533 | device, 534 | flags: dev_info.flags, 535 | num_regions: dev_info.num_regions, 536 | num_irqs: dev_info.num_irqs, 537 | } 538 | } 539 | 540 | fn get_irqs(&self) -> Result> { 541 | let mut irqs: HashMap = HashMap::new(); 542 | 543 | for index in 0..self.num_irqs { 544 | let mut irq_info = vfio_irq_info { 545 | argsz: mem::size_of::() as u32, 546 | flags: 0, 547 | index, 548 | count: 0, 549 | }; 550 | 551 | if vfio_syscall::get_device_irq_info(self, &mut irq_info).is_err() { 552 | warn!("Could not get VFIO IRQ info for index {:}", index); 553 | continue; 554 | } 555 | 556 | let irq = VfioIrq { 557 | flags: irq_info.flags, 558 | index, 559 | count: irq_info.count, 560 | }; 561 | 562 | debug!("IRQ #{}", index); 563 | debug!("\tflag 0x{:x}", irq.flags); 564 | debug!("\tindex {}", irq.index); 565 | debug!("\tcount {}", irq.count); 566 | irqs.insert(index, irq); 567 | } 568 | 569 | Ok(irqs) 570 | } 571 | 572 | fn get_region_map( 573 | &self, 574 | region: &mut VfioRegion, 575 | region_info: &vfio_region_info, 576 | ) -> Result<()> { 577 | let region_info_size: u32 = mem::size_of::() as u32; 578 | 579 | if region_info.flags & VFIO_REGION_INFO_FLAG_CAPS == 0 580 | || region_info.argsz <= region_info_size 581 | { 582 | // There is not capabilities information for that region, we can just return. 583 | return Ok(()); 584 | } 585 | 586 | // There is a capability information for that region, we have to call 587 | // VFIO_DEVICE_GET_REGION_INFO with a vfio_region_with_cap structure and the hinted size. 588 | let mut region_with_cap = vfio_region_info_with_cap::from_region_info(region_info); 589 | vfio_syscall::get_device_region_info_cap(self, &mut region_with_cap)?; 590 | 591 | // region_with_cap[0] may contain different types of structure depending on the capability 592 | // type, but all of them begin with vfio_info_cap_header in order to identify the capability 593 | // type, version and if there's another capability after this one. 594 | // It is safe to convert region_with_cap[0] with an offset of cap_offset into 595 | // vfio_info_cap_header pointer and access its elements, as long as cap_offset is greater 596 | // than region_info_size. 597 | // 598 | // Safety: following code is safe because we trust data returned by the kernel. 599 | if region_with_cap[0].region_info.cap_offset >= region_info_size { 600 | let mut next_cap_offset = region_with_cap[0].region_info.cap_offset; 601 | let info_ptr = ®ion_with_cap[0] as *const vfio_region_info_with_cap as *const u8; 602 | 603 | while next_cap_offset >= region_info_size { 604 | let cap_header = unsafe { 605 | *(info_ptr.offset(next_cap_offset as isize) as *const vfio_info_cap_header) 606 | }; 607 | 608 | match u32::from(cap_header.id) { 609 | VFIO_REGION_INFO_CAP_SPARSE_MMAP => { 610 | let sparse_mmap = unsafe { 611 | info_ptr.offset(next_cap_offset as isize) 612 | as *const vfio_region_info_cap_sparse_mmap 613 | }; 614 | let nr_areas = unsafe { (*sparse_mmap).nr_areas }; 615 | let areas = unsafe { (*sparse_mmap).areas.as_slice(nr_areas as usize) }; 616 | 617 | let cap = VfioRegionInfoCapSparseMmap { 618 | areas: areas 619 | .iter() 620 | .map(|a| VfioRegionSparseMmapArea { 621 | offset: a.offset, 622 | size: a.size, 623 | }) 624 | .collect(), 625 | }; 626 | region.caps.push(VfioRegionInfoCap::SparseMmap(cap)); 627 | } 628 | VFIO_REGION_INFO_CAP_TYPE => { 629 | let type_ = unsafe { 630 | *(info_ptr.offset(next_cap_offset as isize) 631 | as *const vfio_region_info_cap_type) 632 | }; 633 | let cap = VfioRegionInfoCapType { 634 | type_: type_.type_, 635 | subtype: type_.subtype, 636 | }; 637 | region.caps.push(VfioRegionInfoCap::Type(cap)); 638 | } 639 | VFIO_REGION_INFO_CAP_MSIX_MAPPABLE => { 640 | region.caps.push(VfioRegionInfoCap::MsixMappable); 641 | } 642 | VFIO_REGION_INFO_CAP_NVLINK2_SSATGT => { 643 | let nvlink2_ssatgt = unsafe { 644 | *(info_ptr.offset(next_cap_offset as isize) 645 | as *const vfio_region_info_cap_nvlink2_ssatgt) 646 | }; 647 | let cap = VfioRegionInfoCapNvlink2Ssatgt { 648 | tgt: nvlink2_ssatgt.tgt, 649 | }; 650 | region.caps.push(VfioRegionInfoCap::Nvlink2Ssatgt(cap)); 651 | } 652 | VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD => { 653 | let nvlink2_lnkspd = unsafe { 654 | *(info_ptr.offset(next_cap_offset as isize) 655 | as *const vfio_region_info_cap_nvlink2_lnkspd) 656 | }; 657 | let cap = VfioRegionInfoCapNvlink2Lnkspd { 658 | link_speed: nvlink2_lnkspd.link_speed, 659 | }; 660 | region.caps.push(VfioRegionInfoCap::Nvlink2Lnkspd(cap)); 661 | } 662 | _ => {} 663 | } 664 | 665 | next_cap_offset = cap_header.next; 666 | } 667 | } 668 | 669 | Ok(()) 670 | } 671 | 672 | fn get_regions(&self) -> Result> { 673 | let mut regions: Vec = Vec::new(); 674 | 675 | for i in VFIO_PCI_BAR0_REGION_INDEX..self.num_regions { 676 | let argsz: u32 = mem::size_of::() as u32; 677 | let mut reg_info = vfio_region_info { 678 | argsz, 679 | flags: 0, 680 | index: i, 681 | cap_offset: 0, 682 | size: 0, 683 | offset: 0, 684 | }; 685 | 686 | if vfio_syscall::get_device_region_info(self, &mut reg_info).is_err() { 687 | error!("Could not get region #{} info", i); 688 | continue; 689 | } 690 | 691 | let mut region = VfioRegion { 692 | flags: reg_info.flags, 693 | size: reg_info.size, 694 | offset: reg_info.offset, 695 | caps: Vec::new(), 696 | }; 697 | if let Err(e) = self.get_region_map(&mut region, ®_info) { 698 | error!("Could not get region #{} map {}", i, e); 699 | continue; 700 | } 701 | 702 | debug!("Region #{}", i); 703 | debug!("\tflag 0x{:x}", region.flags); 704 | debug!("\tsize 0x{:x}", region.size); 705 | debug!("\toffset 0x{:x}", region.offset); 706 | regions.push(region); 707 | } 708 | 709 | Ok(regions) 710 | } 711 | } 712 | 713 | impl AsRawFd for VfioDeviceInfo { 714 | fn as_raw_fd(&self) -> RawFd { 715 | self.device.as_raw_fd() 716 | } 717 | } 718 | 719 | /// A safe wrapper over a Vfio device to access underlying hardware device. 720 | /// 721 | /// The VFIO device API includes ioctls for describing the device, the I/O regions and their 722 | /// read/write/mmap offsets on the device descriptor, as well as mechanisms for describing and 723 | /// registering interrupt notifications. 724 | pub struct VfioDevice { 725 | pub(crate) device: ManuallyDrop, 726 | pub(crate) flags: u32, 727 | pub(crate) regions: Vec, 728 | pub(crate) irqs: HashMap, 729 | pub(crate) group: Arc, 730 | pub(crate) container: Arc, 731 | } 732 | 733 | impl VfioDevice { 734 | #[cfg(not(test))] 735 | fn get_group_id_from_path(sysfspath: &Path) -> Result { 736 | let uuid_path: PathBuf = [sysfspath, Path::new("iommu_group")].iter().collect(); 737 | let group_path = uuid_path.read_link().map_err(|_| VfioError::InvalidPath)?; 738 | let group_osstr = group_path.file_name().ok_or(VfioError::InvalidPath)?; 739 | let group_str = group_osstr.to_str().ok_or(VfioError::InvalidPath)?; 740 | 741 | group_str.parse::().map_err(|_| VfioError::InvalidPath) 742 | } 743 | 744 | /// Create a new vfio device, then guest read/write on this device could be transferred into kernel vfio. 745 | /// 746 | /// # Parameters 747 | /// * `sysfspath`: specify the vfio device path in sys file system. 748 | /// * `container`: the new VFIO device object will bind to this container object. 749 | pub fn new(sysfspath: &Path, container: Arc) -> Result { 750 | let group_id = Self::get_group_id_from_path(sysfspath)?; 751 | let group = container.get_group(group_id)?; 752 | let device_info = group.get_device(sysfspath)?; 753 | let regions = device_info.get_regions()?; 754 | let irqs = device_info.get_irqs()?; 755 | 756 | Ok(VfioDevice { 757 | device: ManuallyDrop::new(device_info.device), 758 | flags: device_info.flags, 759 | regions, 760 | irqs, 761 | group, 762 | container, 763 | }) 764 | } 765 | 766 | /// VFIO device reset only if the device supports being reset. 767 | pub fn reset(&self) { 768 | if self.flags & VFIO_DEVICE_FLAGS_RESET != 0 { 769 | vfio_syscall::reset(self); 770 | } 771 | } 772 | 773 | /// Get information about VFIO IRQs. 774 | /// 775 | /// # Arguments 776 | /// * `irq_index` - The type (INTX, MSI or MSI-X) of interrupts to enable. 777 | pub fn get_irq_info(&self, irq_index: u32) -> Option<&VfioIrq> { 778 | self.irqs.get(&irq_index) 779 | } 780 | 781 | /// Trigger a VFIO device IRQ from userspace. 782 | /// 783 | /// Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used with ACTION_TRIGGER 784 | /// to perform kernel level interrupt loopback testing from userspace (ie. simulate hardware 785 | /// triggering). 786 | /// 787 | /// # Arguments 788 | /// * `irq_index` - The type (INTX, MSI or MSI-X) of interrupts to enable. 789 | /// * `vector` - The sub-index into the interrupt group of `irq_index`. 790 | pub fn trigger_irq(&self, irq_index: u32, vector: u32) -> Result<()> { 791 | let irq = self 792 | .irqs 793 | .get(&irq_index) 794 | .ok_or(VfioError::VfioDeviceTriggerIrq)?; 795 | if irq.count <= vector { 796 | return Err(VfioError::VfioDeviceTriggerIrq); 797 | } 798 | 799 | let mut irq_set = vec_with_array_field::(0); 800 | irq_set[0].argsz = mem::size_of::() as u32; 801 | irq_set[0].flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER; 802 | irq_set[0].index = irq_index; 803 | irq_set[0].start = vector; 804 | irq_set[0].count = 1; 805 | 806 | vfio_syscall::set_device_irqs(self, irq_set.as_slice()) 807 | .map_err(|_| VfioError::VfioDeviceTriggerIrq) 808 | } 809 | 810 | /// Enables a VFIO device IRQs. 811 | /// This maps a vector of EventFds to all VFIO managed interrupts. In other words, this 812 | /// tells VFIO which EventFd to write into whenever one of the device interrupt vector 813 | /// is triggered. 814 | /// 815 | /// # Arguments 816 | /// * `irq_index` - The type (INTX, MSI or MSI-X) of interrupts to enable. 817 | /// * `event_fds` - The EventFds vector that matches all the supported VFIO interrupts. 818 | pub fn enable_irq(&self, irq_index: u32, event_fds: Vec<&EventFd>) -> Result<()> { 819 | let irq = self 820 | .irqs 821 | .get(&irq_index) 822 | .ok_or(VfioError::VfioDeviceEnableIrq)?; 823 | if irq.count == 0 || (irq.count as usize) < event_fds.len() { 824 | return Err(VfioError::VfioDeviceEnableIrq); 825 | } 826 | 827 | let mut irq_set = vec_with_array_field::(event_fds.len()); 828 | irq_set[0].argsz = mem::size_of::() as u32 829 | + (event_fds.len() * mem::size_of::()) as u32; 830 | irq_set[0].flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 831 | irq_set[0].index = irq_index; 832 | irq_set[0].start = 0; 833 | irq_set[0].count = event_fds.len() as u32; 834 | 835 | { 836 | // irq_set.data could be none, bool or fd according to flags, so irq_set.data 837 | // is u8 default, here irq_set.data is a vector of fds as u32, so 4 default u8 838 | // are combined together as u32 for each fd. 839 | // It is safe as enough space is reserved through 840 | // vec_with_array_field(u32). 841 | let fds = unsafe { 842 | irq_set[0] 843 | .data 844 | .as_mut_slice(event_fds.len() * mem::size_of::()) 845 | }; 846 | for (index, event_fd) in event_fds.iter().enumerate() { 847 | let fds_offset = index * mem::size_of::(); 848 | let fd = &mut fds[fds_offset..fds_offset + mem::size_of::()]; 849 | LittleEndian::write_u32(fd, event_fd.as_raw_fd() as u32); 850 | } 851 | } 852 | 853 | vfio_syscall::set_device_irqs(self, irq_set.as_slice()) 854 | .map_err(|_| VfioError::VfioDeviceEnableIrq) 855 | } 856 | 857 | /// Disables a VFIO device IRQs 858 | /// 859 | /// # Arguments 860 | /// * `irq_index` - The type (INTX, MSI or MSI-X) of interrupts to disable. 861 | pub fn disable_irq(&self, irq_index: u32) -> Result<()> { 862 | let irq = self 863 | .irqs 864 | .get(&irq_index) 865 | .ok_or(VfioError::VfioDeviceDisableIrq)?; 866 | // Currently the VFIO driver only support MASK/UNMASK INTX, so count is hard-coded to 1. 867 | if irq.count == 0 { 868 | return Err(VfioError::VfioDeviceDisableIrq); 869 | } 870 | 871 | // Individual subindex interrupts can be disabled using the -1 value for DATA_EVENTFD or 872 | // the index can be disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0. 873 | let mut irq_set = vec_with_array_field::(0); 874 | irq_set[0].argsz = mem::size_of::() as u32; 875 | irq_set[0].flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER; 876 | irq_set[0].index = irq_index; 877 | irq_set[0].start = 0; 878 | irq_set[0].count = 0; 879 | 880 | vfio_syscall::set_device_irqs(self, irq_set.as_slice()) 881 | .map_err(|_| VfioError::VfioDeviceDisableIrq) 882 | } 883 | 884 | /// Unmask IRQ 885 | /// 886 | /// # Arguments 887 | /// * `irq_index` - The type (INTX, MSI or MSI-X) of interrupts to unmask. 888 | pub fn unmask_irq(&self, irq_index: u32) -> Result<()> { 889 | let irq = self 890 | .irqs 891 | .get(&irq_index) 892 | .ok_or(VfioError::VfioDeviceUnmaskIrq)?; 893 | // Currently the VFIO driver only support MASK/UNMASK INTX, so count is hard-coded to 1. 894 | if irq.count == 0 || irq.count != 1 || irq.index != VFIO_PCI_INTX_IRQ_INDEX { 895 | return Err(VfioError::VfioDeviceUnmaskIrq); 896 | } 897 | 898 | let mut irq_set = vec_with_array_field::(0); 899 | irq_set[0].argsz = mem::size_of::() as u32; 900 | irq_set[0].flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK; 901 | irq_set[0].index = irq_index; 902 | irq_set[0].start = 0; 903 | irq_set[0].count = 1; 904 | 905 | vfio_syscall::set_device_irqs(self, irq_set.as_slice()) 906 | .map_err(|_| VfioError::VfioDeviceUnmaskIrq) 907 | } 908 | 909 | /// Wrapper to enable MSI IRQs. 910 | pub fn enable_msi(&self, fds: Vec<&EventFd>) -> Result<()> { 911 | self.enable_irq(VFIO_PCI_MSI_IRQ_INDEX, fds) 912 | } 913 | 914 | /// Wrapper to disable MSI IRQs. 915 | pub fn disable_msi(&self) -> Result<()> { 916 | self.disable_irq(VFIO_PCI_MSI_IRQ_INDEX) 917 | } 918 | 919 | /// Wrapper to enable MSI-X IRQs. 920 | pub fn enable_msix(&self, fds: Vec<&EventFd>) -> Result<()> { 921 | self.enable_irq(VFIO_PCI_MSIX_IRQ_INDEX, fds) 922 | } 923 | 924 | /// Wrapper to disable MSI-X IRQs. 925 | pub fn disable_msix(&self) -> Result<()> { 926 | self.disable_irq(VFIO_PCI_MSIX_IRQ_INDEX) 927 | } 928 | 929 | /// Get a region's flag. 930 | /// 931 | /// # Arguments 932 | /// * `index` - The index of memory region. 933 | pub fn get_region_flags(&self, index: u32) -> u32 { 934 | match self.regions.get(index as usize) { 935 | Some(v) => v.flags, 936 | None => 0, 937 | } 938 | } 939 | 940 | /// Get a region's offset. 941 | /// 942 | /// # Arguments 943 | /// * `index` - The index of memory region. 944 | pub fn get_region_offset(&self, index: u32) -> u64 { 945 | match self.regions.get(index as usize) { 946 | Some(v) => v.offset, 947 | None => 0, 948 | } 949 | } 950 | 951 | /// Get a region's size. 952 | /// 953 | /// # Arguments 954 | /// * `index` - The index of memory region. 955 | pub fn get_region_size(&self, index: u32) -> u64 { 956 | match self.regions.get(index as usize) { 957 | Some(v) => v.size, 958 | None => { 959 | warn!("get_region_size with invalid index: {}", index); 960 | 0 961 | } 962 | } 963 | } 964 | 965 | /// Get region's list of capabilities 966 | /// 967 | /// # Arguments 968 | /// * `index` - The index of memory region. 969 | pub fn get_region_caps(&self, index: u32) -> Vec { 970 | match self.regions.get(index as usize) { 971 | Some(v) => v.caps.clone(), 972 | None => { 973 | warn!("get_region_caps with invalid index: {}", index); 974 | Vec::new() 975 | } 976 | } 977 | } 978 | 979 | /// Read region's data from VFIO device into buf 980 | /// 981 | /// # Arguments 982 | /// * `index`: region num 983 | /// * `buf`: data destination and buf length is read size 984 | /// * `addr`: offset in the region 985 | pub fn region_read(&self, index: u32, buf: &mut [u8], addr: u64) { 986 | let region: &VfioRegion; 987 | match self.regions.get(index as usize) { 988 | Some(v) => region = v, 989 | None => { 990 | warn!("region read with invalid index: {}", index); 991 | return; 992 | } 993 | } 994 | 995 | let size = buf.len() as u64; 996 | if size > region.size || addr + size > region.size { 997 | warn!( 998 | "region read with invalid parameter, add: {}, size: {}", 999 | addr, size 1000 | ); 1001 | return; 1002 | } 1003 | 1004 | if let Err(e) = self.device.read_exact_at(buf, region.offset + addr) { 1005 | warn!( 1006 | "Failed to read region in index: {}, addr: {}, error: {}", 1007 | index, addr, e 1008 | ); 1009 | } 1010 | } 1011 | 1012 | /// Write the data from buf into a vfio device region 1013 | /// 1014 | /// # Arguments 1015 | /// * `index`: region num 1016 | /// * `buf`: data src and buf length is write size 1017 | /// * `addr`: offset in the region 1018 | pub fn region_write(&self, index: u32, buf: &[u8], addr: u64) { 1019 | let stub: &VfioRegion; 1020 | match self.regions.get(index as usize) { 1021 | Some(v) => stub = v, 1022 | None => { 1023 | warn!("region write with invalid index: {}", index); 1024 | return; 1025 | } 1026 | } 1027 | 1028 | let size = buf.len() as u64; 1029 | if size > stub.size 1030 | || addr + size > stub.size 1031 | || (stub.flags & VFIO_REGION_INFO_FLAG_WRITE) == 0 1032 | { 1033 | warn!( 1034 | "region write with invalid parameter, add: {}, size: {}", 1035 | addr, size 1036 | ); 1037 | return; 1038 | } 1039 | 1040 | if let Err(e) = self.device.write_all_at(buf, stub.offset + addr) { 1041 | warn!( 1042 | "Failed to write region in index: {}, addr: {}, error: {}", 1043 | index, addr, e 1044 | ); 1045 | } 1046 | } 1047 | 1048 | /// Return the maximum numner of interrupts a VFIO device can request. 1049 | pub fn max_interrupts(&self) -> u32 { 1050 | let mut max_interrupts = 0; 1051 | let irq_indexes = vec![ 1052 | VFIO_PCI_INTX_IRQ_INDEX, 1053 | VFIO_PCI_MSI_IRQ_INDEX, 1054 | VFIO_PCI_MSIX_IRQ_INDEX, 1055 | ]; 1056 | 1057 | for index in irq_indexes { 1058 | if let Some(irq_info) = self.irqs.get(&index) { 1059 | if irq_info.count > max_interrupts { 1060 | max_interrupts = irq_info.count; 1061 | } 1062 | } 1063 | } 1064 | 1065 | max_interrupts 1066 | } 1067 | } 1068 | 1069 | impl AsRawFd for VfioDevice { 1070 | fn as_raw_fd(&self) -> RawFd { 1071 | self.device.as_raw_fd() 1072 | } 1073 | } 1074 | 1075 | impl Drop for VfioDevice { 1076 | fn drop(&mut self) { 1077 | // Safe because we own the File object. 1078 | // ManuallyDrop is needed here because we need to ensure that VfioDevice::device is closed 1079 | // before dropping VfioDevice::group, otherwise it will cause EBUSY when putting the 1080 | // group object. 1081 | unsafe { 1082 | ManuallyDrop::drop(&mut self.device); 1083 | } 1084 | self.container.put_group(self.group.clone()); 1085 | } 1086 | } 1087 | 1088 | #[cfg(test)] 1089 | mod tests { 1090 | use super::*; 1091 | use std::mem::size_of; 1092 | use vmm_sys_util::tempfile::TempFile; 1093 | 1094 | impl VfioGroup { 1095 | pub(crate) fn open_group_file(id: u32) -> Result { 1096 | let tmp_file = TempFile::new().unwrap(); 1097 | OpenOptions::new() 1098 | .read(true) 1099 | .write(true) 1100 | .open(tmp_file.as_path()) 1101 | .map_err(|e| VfioError::OpenGroup(e, id.to_string())) 1102 | } 1103 | } 1104 | 1105 | impl VfioDevice { 1106 | pub(crate) fn get_group_id_from_path(_sysfspath: &Path) -> Result { 1107 | Ok(3) 1108 | } 1109 | } 1110 | 1111 | #[test] 1112 | fn test_vfio_region_info_with_cap() { 1113 | let reg = vfio_region_info { 1114 | argsz: 129, 1115 | flags: 0, 1116 | index: 5, 1117 | cap_offset: 0, 1118 | size: 0, 1119 | offset: 0, 1120 | }; 1121 | let cap = vfio_region_info_with_cap::from_region_info(®); 1122 | 1123 | assert_eq!(size_of::(), 32); 1124 | assert_eq!(cap.len(), 5); 1125 | assert_eq!(cap[0].region_info.argsz, 129); 1126 | assert_eq!(cap[0].region_info.index, 5); 1127 | 1128 | let reg = vfio_region_info_with_cap::default(); 1129 | assert_eq!(reg.region_info.index, 0); 1130 | assert_eq!(reg.region_info.argsz, 0); 1131 | } 1132 | 1133 | #[test] 1134 | fn test_vfio_device_info() { 1135 | let tmp_file = TempFile::new().unwrap(); 1136 | let device = File::open(tmp_file.as_path()).unwrap(); 1137 | let dev_info = vfio_syscall::create_dev_info_for_test(); 1138 | let device_info = VfioDeviceInfo::new(device, &dev_info); 1139 | 1140 | let irqs = device_info.get_irqs().unwrap(); 1141 | assert_eq!(irqs.len(), 3); 1142 | let irq = irqs.get(&0).unwrap(); 1143 | assert_eq!(irq.flags, VFIO_IRQ_INFO_MASKABLE); 1144 | assert_eq!(irq.count, 1); 1145 | assert_eq!(irq.index, 0); 1146 | let irq = irqs.get(&1).unwrap(); 1147 | assert_eq!(irq.flags, VFIO_IRQ_INFO_EVENTFD); 1148 | assert_eq!(irq.count, 32); 1149 | assert_eq!(irq.index, 1); 1150 | let irq = irqs.get(&2).unwrap(); 1151 | assert_eq!(irq.flags, VFIO_IRQ_INFO_EVENTFD); 1152 | assert_eq!(irq.count, 2048); 1153 | assert_eq!(irq.index, 2); 1154 | 1155 | let regions = device_info.get_regions().unwrap(); 1156 | assert_eq!(regions.len(), 2); 1157 | assert_eq!(regions[0].flags, 0); 1158 | assert_eq!(regions[0].offset, 0x10000); 1159 | assert_eq!(regions[0].size, 0x1000); 1160 | assert_eq!(regions[0].caps.len(), 0); 1161 | 1162 | assert_eq!(regions[1].flags, VFIO_REGION_INFO_FLAG_CAPS); 1163 | assert_eq!(regions[1].offset, 0x20000); 1164 | assert_eq!(regions[1].size, 0x2000); 1165 | assert_eq!(regions[1].caps.len(), 3); 1166 | assert_eq!(regions[1].caps[0], VfioRegionInfoCap::MsixMappable); 1167 | 1168 | let ty = ®ions[1].caps[1]; 1169 | if let VfioRegionInfoCap::Type(t) = ty { 1170 | assert_eq!(t.type_, 0x5); 1171 | assert_eq!(t.subtype, 0x6); 1172 | } else { 1173 | panic!("expect VfioRegionInfoCapType"); 1174 | } 1175 | 1176 | let mmap = ®ions[1].caps[2]; 1177 | if let VfioRegionInfoCap::SparseMmap(m) = mmap { 1178 | assert_eq!(m.areas.len(), 1); 1179 | assert_eq!(m.areas[0].size, 0x3); 1180 | assert_eq!(m.areas[0].offset, 0x4); 1181 | } else { 1182 | panic!("expect VfioRegionInfoCapType"); 1183 | } 1184 | } 1185 | 1186 | fn create_vfio_container() -> VfioContainer { 1187 | let tmp_file = TempFile::new().unwrap(); 1188 | let container = File::open(tmp_file.as_path()).unwrap(); 1189 | 1190 | VfioContainer { 1191 | container, 1192 | device_fd: (), 1193 | groups: Mutex::new(HashMap::new()), 1194 | } 1195 | } 1196 | 1197 | #[test] 1198 | fn test_vfio_container() { 1199 | let container = create_vfio_container(); 1200 | 1201 | assert!(container.as_raw_fd() > 0); 1202 | container.check_api_version().unwrap(); 1203 | container.check_extension(VFIO_TYPE1v2_IOMMU).unwrap(); 1204 | 1205 | let group = VfioGroup::new(1).unwrap(); 1206 | container.device_add_group(&group).unwrap(); 1207 | container.device_del_group(&group).unwrap(); 1208 | 1209 | let group = container.get_group(3).unwrap(); 1210 | assert_eq!(Arc::strong_count(&group), 2); 1211 | assert_eq!(container.groups.lock().unwrap().len(), 1); 1212 | let group2 = container.get_group(4).unwrap(); 1213 | assert_eq!(Arc::strong_count(&group2), 2); 1214 | assert_eq!(container.groups.lock().unwrap().len(), 2); 1215 | 1216 | let group3 = container.get_group(3).unwrap(); 1217 | assert_eq!(Arc::strong_count(&group), 3); 1218 | let group4 = container.get_group(3).unwrap(); 1219 | assert_eq!(Arc::strong_count(&group), 4); 1220 | container.put_group(group4); 1221 | assert_eq!(Arc::strong_count(&group), 3); 1222 | container.put_group(group3); 1223 | assert_eq!(Arc::strong_count(&group), 1); 1224 | 1225 | container.vfio_dma_map(0x1000, 0x1000, 0x8000).unwrap(); 1226 | container.vfio_dma_map(0x2000, 0x2000, 0x8000).unwrap_err(); 1227 | container.vfio_dma_unmap(0x1000, 0x1000).unwrap(); 1228 | container.vfio_dma_unmap(0x2000, 0x2000).unwrap_err(); 1229 | } 1230 | 1231 | #[test] 1232 | fn test_vfio_group() { 1233 | let group = VfioGroup::new(1).unwrap(); 1234 | let tmp_file = TempFile::new().unwrap(); 1235 | 1236 | assert_eq!(group.id, 1); 1237 | assert!(group.as_raw_fd() >= 0); 1238 | let device = group.get_device(tmp_file.as_path()).unwrap(); 1239 | assert_eq!(device.num_irqs, 3); 1240 | assert_eq!(device.num_regions, 8); 1241 | 1242 | let regions = device.get_regions().unwrap(); 1243 | assert_eq!(regions.len(), 7) 1244 | } 1245 | 1246 | #[test] 1247 | fn test_vfio_device() { 1248 | let tmp_file = TempFile::new().unwrap(); 1249 | let container = Arc::new(create_vfio_container()); 1250 | let device = VfioDevice::new(tmp_file.as_path(), container.clone()).unwrap(); 1251 | 1252 | assert!(device.as_raw_fd() > 0); 1253 | assert_eq!(device.max_interrupts(), 2048); 1254 | 1255 | device.reset(); 1256 | assert_eq!(device.regions.len(), 7); 1257 | assert_eq!(device.irqs.len(), 3); 1258 | 1259 | assert!(device.get_irq_info(3).is_none()); 1260 | let irq = device.get_irq_info(2).unwrap(); 1261 | assert_eq!(irq.count, 2048); 1262 | 1263 | device.trigger_irq(3, 0).unwrap_err(); 1264 | device.trigger_irq(2, 2048).unwrap_err(); 1265 | device.trigger_irq(2, 2047).unwrap(); 1266 | device.trigger_irq(2, 0).unwrap(); 1267 | 1268 | device.enable_irq(3, Vec::new()).unwrap_err(); 1269 | device.enable_irq(0, Vec::new()).unwrap_err(); 1270 | device.enable_irq(1, Vec::new()).unwrap(); 1271 | 1272 | device.disable_irq(3).unwrap_err(); 1273 | device.disable_irq(0).unwrap_err(); 1274 | device.disable_irq(1).unwrap(); 1275 | 1276 | device.unmask_irq(3).unwrap_err(); 1277 | device.unmask_irq(1).unwrap_err(); 1278 | device.unmask_irq(0).unwrap(); 1279 | 1280 | device.enable_msi(Vec::new()).unwrap(); 1281 | device.disable_msi().unwrap(); 1282 | device.enable_msix(Vec::new()).unwrap(); 1283 | device.disable_msix().unwrap(); 1284 | 1285 | assert_eq!(device.get_region_flags(1), VFIO_REGION_INFO_FLAG_CAPS); 1286 | assert_eq!(device.get_region_flags(7), 0); 1287 | assert_eq!(device.get_region_offset(1), 0x20000); 1288 | assert_eq!(device.get_region_offset(7), 0); 1289 | assert_eq!(device.get_region_size(1), 0x2000); 1290 | assert_eq!(device.get_region_size(7), 0); 1291 | assert_eq!(device.get_region_caps(1).len(), 3); 1292 | assert_eq!(device.get_region_caps(7).len(), 0); 1293 | 1294 | let mut buf = [0u8; 16]; 1295 | device.region_read(7, &mut buf, 0x30000); 1296 | device.region_read(1, &mut buf, 0x30000); 1297 | device.region_write(7, &buf, 0x30000); 1298 | device.region_write(1, &buf, 0x30000); 1299 | 1300 | device.reset(); 1301 | 1302 | drop(device); 1303 | assert_eq!(container.groups.lock().unwrap().len(), 0); 1304 | } 1305 | 1306 | #[test] 1307 | #[allow(clippy::redundant_clone)] 1308 | fn test_vfio_region_info_cap() { 1309 | let v1 = VfioRegionInfoCap::Type(VfioRegionInfoCapType { 1310 | type_: 1, 1311 | subtype: 1, 1312 | }); 1313 | let v2 = VfioRegionInfoCap::Type(VfioRegionInfoCapType { 1314 | type_: 1, 1315 | subtype: 2, 1316 | }); 1317 | 1318 | assert_eq!(v1, v1.clone()); 1319 | assert_ne!(v1, v2); 1320 | 1321 | let v3 = VfioRegionInfoCap::SparseMmap(VfioRegionInfoCapSparseMmap { 1322 | areas: vec![VfioRegionSparseMmapArea { offset: 3, size: 4 }], 1323 | }); 1324 | let v4 = VfioRegionInfoCap::SparseMmap(VfioRegionInfoCapSparseMmap { 1325 | areas: vec![VfioRegionSparseMmapArea { offset: 5, size: 6 }], 1326 | }); 1327 | assert_eq!(v3, v3.clone()); 1328 | assert_ne!(v3, v4); 1329 | assert_ne!(v1, v4); 1330 | assert_ne!(v1.clone(), v4); 1331 | 1332 | let v5 = VfioRegionInfoCap::MsixMappable; 1333 | assert_eq!(v5, v5.clone()); 1334 | assert_ne!(v5, v1); 1335 | assert_ne!(v5, v3); 1336 | assert_ne!(v5, v2.clone()); 1337 | assert_ne!(v5, v4.clone()); 1338 | 1339 | let v6 = VfioRegionInfoCap::Nvlink2Lnkspd(VfioRegionInfoCapNvlink2Lnkspd { link_speed: 7 }); 1340 | let v7 = VfioRegionInfoCap::Nvlink2Lnkspd(VfioRegionInfoCapNvlink2Lnkspd { link_speed: 8 }); 1341 | assert_eq!(v6, v6.clone()); 1342 | assert_ne!(v6, v7); 1343 | assert_ne!(v6, v1); 1344 | assert_ne!(v6, v2.clone()); 1345 | assert_ne!(v6, v4.clone()); 1346 | 1347 | let v8 = VfioRegionInfoCap::Nvlink2Ssatgt(VfioRegionInfoCapNvlink2Ssatgt { tgt: 9 }); 1348 | let v9 = VfioRegionInfoCap::Nvlink2Ssatgt(VfioRegionInfoCapNvlink2Ssatgt { tgt: 10 }); 1349 | assert_eq!(v8, v8.clone()); 1350 | assert_ne!(v8, v9); 1351 | assert_ne!(v8, v1); 1352 | assert_ne!(v8, v2.clone()); 1353 | assert_ne!(v8, v4.clone()); 1354 | assert_ne!(v8, v6.clone()); 1355 | } 1356 | } 1357 | -------------------------------------------------------------------------------- /crates/vfio-ioctls/src/vfio_ioctls.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 4 | // 5 | #![allow(non_upper_case_globals)] 6 | #![allow(non_camel_case_types)] 7 | #![allow(non_snake_case)] 8 | 9 | use std::ffi::CStr; 10 | use std::fs::File; 11 | use std::mem::size_of; 12 | use std::os::unix::io::AsRawFd; 13 | 14 | use vfio_bindings::bindings::vfio::*; 15 | use vmm_sys_util::errno::Error as SysError; 16 | 17 | use crate::vfio_device::{vfio_region_info_with_cap, VfioDeviceInfo}; 18 | use crate::{Result, VfioContainer, VfioDevice, VfioError, VfioGroup}; 19 | 20 | ioctl_io_nr!(VFIO_GET_API_VERSION, VFIO_TYPE, VFIO_BASE); 21 | ioctl_io_nr!(VFIO_CHECK_EXTENSION, VFIO_TYPE, VFIO_BASE + 1); 22 | ioctl_io_nr!(VFIO_SET_IOMMU, VFIO_TYPE, VFIO_BASE + 2); 23 | ioctl_io_nr!(VFIO_GROUP_GET_STATUS, VFIO_TYPE, VFIO_BASE + 3); 24 | ioctl_io_nr!(VFIO_GROUP_SET_CONTAINER, VFIO_TYPE, VFIO_BASE + 4); 25 | ioctl_io_nr!(VFIO_GROUP_UNSET_CONTAINER, VFIO_TYPE, VFIO_BASE + 5); 26 | ioctl_io_nr!(VFIO_GROUP_GET_DEVICE_FD, VFIO_TYPE, VFIO_BASE + 6); 27 | ioctl_io_nr!(VFIO_DEVICE_GET_INFO, VFIO_TYPE, VFIO_BASE + 7); 28 | ioctl_io_nr!(VFIO_DEVICE_GET_REGION_INFO, VFIO_TYPE, VFIO_BASE + 8); 29 | ioctl_io_nr!(VFIO_DEVICE_GET_IRQ_INFO, VFIO_TYPE, VFIO_BASE + 9); 30 | ioctl_io_nr!(VFIO_DEVICE_SET_IRQS, VFIO_TYPE, VFIO_BASE + 10); 31 | ioctl_io_nr!(VFIO_DEVICE_RESET, VFIO_TYPE, VFIO_BASE + 11); 32 | ioctl_io_nr!( 33 | VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, 34 | VFIO_TYPE, 35 | VFIO_BASE + 12 36 | ); 37 | ioctl_io_nr!(VFIO_DEVICE_PCI_HOT_RESET, VFIO_TYPE, VFIO_BASE + 13); 38 | ioctl_io_nr!(VFIO_DEVICE_QUERY_GFX_PLANE, VFIO_TYPE, VFIO_BASE + 14); 39 | ioctl_io_nr!(VFIO_DEVICE_GET_GFX_DMABUF, VFIO_TYPE, VFIO_BASE + 15); 40 | ioctl_io_nr!(VFIO_DEVICE_IOEVENTFD, VFIO_TYPE, VFIO_BASE + 16); 41 | ioctl_io_nr!(VFIO_IOMMU_GET_INFO, VFIO_TYPE, VFIO_BASE + 12); 42 | ioctl_io_nr!(VFIO_IOMMU_MAP_DMA, VFIO_TYPE, VFIO_BASE + 13); 43 | ioctl_io_nr!(VFIO_IOMMU_UNMAP_DMA, VFIO_TYPE, VFIO_BASE + 14); 44 | ioctl_io_nr!(VFIO_IOMMU_ENABLE, VFIO_TYPE, VFIO_BASE + 15); 45 | ioctl_io_nr!(VFIO_IOMMU_DISABLE, VFIO_TYPE, VFIO_BASE + 16); 46 | 47 | #[cfg(not(test))] 48 | // Safety: 49 | // - absolutely trust the underlying kernel 50 | // - absolutely trust data returned by the underlying kernel 51 | // - assume kernel will return error if caller passes in invalid file handle, parameter or buffer. 52 | pub(crate) mod vfio_syscall { 53 | use super::*; 54 | use std::os::unix::io::FromRawFd; 55 | use vmm_sys_util::ioctl::{ 56 | ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, 57 | }; 58 | 59 | pub(crate) fn check_api_version(container: &VfioContainer) -> i32 { 60 | // Safe as file is vfio container fd and ioctl is defined by kernel. 61 | unsafe { ioctl(container, VFIO_GET_API_VERSION()) } 62 | } 63 | 64 | pub(crate) fn check_extension(container: &VfioContainer, val: u32) -> Result { 65 | // Safe as file is vfio container and make sure val is valid. 66 | let ret = unsafe { ioctl_with_val(container, VFIO_CHECK_EXTENSION(), val.into()) }; 67 | if ret < 0 { 68 | Err(VfioError::VfioExtension) 69 | } else { 70 | Ok(ret as u32) 71 | } 72 | } 73 | 74 | pub(crate) fn set_iommu(container: &VfioContainer, val: u32) -> Result<()> { 75 | // Safe as file is vfio container and make sure val is valid. 76 | let ret = unsafe { ioctl_with_val(container, VFIO_SET_IOMMU(), val.into()) }; 77 | if ret < 0 { 78 | Err(VfioError::ContainerSetIOMMU) 79 | } else { 80 | Ok(()) 81 | } 82 | } 83 | 84 | pub(crate) fn map_dma( 85 | container: &VfioContainer, 86 | dma_map: &vfio_iommu_type1_dma_map, 87 | ) -> Result<()> { 88 | // Safe as file is vfio container, dma_map is constructed by us, and 89 | // we check the return value 90 | let ret = unsafe { ioctl_with_ref(container, VFIO_IOMMU_MAP_DMA(), dma_map) }; 91 | if ret != 0 { 92 | Err(VfioError::IommuDmaMap) 93 | } else { 94 | Ok(()) 95 | } 96 | } 97 | 98 | pub(crate) fn unmap_dma( 99 | container: &VfioContainer, 100 | dma_map: &mut vfio_iommu_type1_dma_unmap, 101 | ) -> Result<()> { 102 | // Safe as file is vfio container, dma_unmap is constructed by us, and 103 | // we check the return value 104 | let ret = unsafe { ioctl_with_ref(container, VFIO_IOMMU_UNMAP_DMA(), dma_map) }; 105 | if ret != 0 { 106 | Err(VfioError::IommuDmaUnmap) 107 | } else { 108 | Ok(()) 109 | } 110 | } 111 | 112 | pub(crate) fn get_group_status( 113 | file: &File, 114 | group_status: &mut vfio_group_status, 115 | ) -> Result<()> { 116 | // Safe as we are the owner of group and group_status which are valid value. 117 | let ret = unsafe { ioctl_with_mut_ref(file, VFIO_GROUP_GET_STATUS(), group_status) }; 118 | if ret < 0 { 119 | Err(VfioError::GetGroupStatus) 120 | } else { 121 | Ok(()) 122 | } 123 | } 124 | 125 | pub(crate) fn get_group_device_fd(group: &VfioGroup, path: &CStr) -> Result { 126 | // Safe as we are the owner of self and path_ptr which are valid value. 127 | let fd = unsafe { ioctl_with_ptr(group, VFIO_GROUP_GET_DEVICE_FD(), path.as_ptr()) }; 128 | if fd < 0 { 129 | Err(VfioError::GroupGetDeviceFD) 130 | } else { 131 | // Safe as fd is valid FD 132 | Ok(unsafe { File::from_raw_fd(fd) }) 133 | } 134 | } 135 | 136 | pub(crate) fn set_group_container(group: &VfioGroup, container: &VfioContainer) -> Result<()> { 137 | let container_raw_fd = container.as_raw_fd(); 138 | // Safe as we are the owner of group and container_raw_fd which are valid value, 139 | // and we verify the ret value 140 | let ret = unsafe { ioctl_with_ref(group, VFIO_GROUP_SET_CONTAINER(), &container_raw_fd) }; 141 | if ret < 0 { 142 | Err(VfioError::GroupSetContainer) 143 | } else { 144 | Ok(()) 145 | } 146 | } 147 | 148 | pub(crate) fn unset_group_container( 149 | group: &VfioGroup, 150 | container: &VfioContainer, 151 | ) -> Result<()> { 152 | let container_raw_fd = container.as_raw_fd(); 153 | // Safe as we are the owner of self and container_raw_fd which are valid value. 154 | let ret = unsafe { ioctl_with_ref(group, VFIO_GROUP_UNSET_CONTAINER(), &container_raw_fd) }; 155 | if ret < 0 { 156 | Err(VfioError::GroupSetContainer) 157 | } else { 158 | Ok(()) 159 | } 160 | } 161 | 162 | pub(crate) fn get_device_info(file: &File, dev_info: &mut vfio_device_info) -> Result<()> { 163 | // Safe as we are the owner of dev and dev_info which are valid value, 164 | // and we verify the return value. 165 | let ret = unsafe { ioctl_with_mut_ref(file, VFIO_DEVICE_GET_INFO(), dev_info) }; 166 | if ret < 0 { 167 | Err(VfioError::VfioDeviceGetInfo) 168 | } else { 169 | Ok(()) 170 | } 171 | } 172 | 173 | pub(crate) fn set_device_irqs(device: &VfioDevice, irq_set: &[vfio_irq_set]) -> Result<()> { 174 | if irq_set.is_empty() 175 | || irq_set[0].argsz as usize > irq_set.len() * size_of::() 176 | { 177 | Err(VfioError::VfioDeviceSetIrq) 178 | } else { 179 | // Safe as we are the owner of self and irq_set which are valid value 180 | let ret = unsafe { ioctl_with_ref(device, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; 181 | if ret < 0 { 182 | Err(VfioError::VfioDeviceSetIrq) 183 | } else { 184 | Ok(()) 185 | } 186 | } 187 | } 188 | 189 | pub(crate) fn reset(device: &VfioDevice) -> i32 { 190 | // Safe as file is vfio device 191 | unsafe { ioctl(device, VFIO_DEVICE_RESET()) } 192 | } 193 | 194 | pub(crate) fn get_device_irq_info( 195 | dev_info: &VfioDeviceInfo, 196 | irq_info: &mut vfio_irq_info, 197 | ) -> Result<()> { 198 | // Safe as we are the owner of dev and irq_info which are valid value 199 | let ret = unsafe { ioctl_with_mut_ref(dev_info, VFIO_DEVICE_GET_IRQ_INFO(), irq_info) }; 200 | if ret < 0 { 201 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::new(-ret))) 202 | } else { 203 | Ok(()) 204 | } 205 | } 206 | 207 | pub(crate) fn get_device_region_info( 208 | dev_info: &VfioDeviceInfo, 209 | reg_info: &mut vfio_region_info, 210 | ) -> Result<()> { 211 | // Safe as we are the owner of dev and region_info which are valid value 212 | // and we verify the return value. 213 | let ret = unsafe { ioctl_with_mut_ref(dev_info, VFIO_DEVICE_GET_REGION_INFO(), reg_info) }; 214 | if ret < 0 { 215 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::new(-ret))) 216 | } else { 217 | Ok(()) 218 | } 219 | } 220 | 221 | pub(crate) fn get_device_region_info_cap( 222 | dev_info: &VfioDeviceInfo, 223 | reg_infos: &mut [vfio_region_info_with_cap], 224 | ) -> Result<()> { 225 | if reg_infos.is_empty() 226 | || reg_infos[0].region_info.argsz as usize 227 | > reg_infos.len() * size_of::() 228 | { 229 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 230 | libc::EINVAL, 231 | ))) 232 | } else { 233 | // Safe as we are the owner of dev and region_info which are valid value, 234 | // and we verify the return value. 235 | let ret = unsafe { 236 | ioctl_with_mut_ref(dev_info, VFIO_DEVICE_GET_REGION_INFO(), &mut reg_infos[0]) 237 | }; 238 | if ret < 0 { 239 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::new(-ret))) 240 | } else { 241 | Ok(()) 242 | } 243 | } 244 | } 245 | } 246 | 247 | #[cfg(test)] 248 | pub(crate) mod vfio_syscall { 249 | use super::*; 250 | use vfio_bindings::bindings::vfio::{vfio_device_info, VFIO_IRQ_INFO_EVENTFD}; 251 | use vmm_sys_util::tempfile::TempFile; 252 | 253 | pub(crate) fn check_api_version(_container: &VfioContainer) -> i32 { 254 | VFIO_API_VERSION as i32 255 | } 256 | 257 | pub(crate) fn check_extension(_container: &VfioContainer, val: u32) -> Result { 258 | if val == VFIO_TYPE1v2_IOMMU { 259 | Ok(1) 260 | } else { 261 | Err(VfioError::VfioExtension) 262 | } 263 | } 264 | 265 | pub(crate) fn set_iommu(_container: &VfioContainer, _val: u32) -> Result<()> { 266 | Ok(()) 267 | } 268 | 269 | pub(crate) fn map_dma( 270 | _container: &VfioContainer, 271 | dma_map: &vfio_iommu_type1_dma_map, 272 | ) -> Result<()> { 273 | if dma_map.iova == 0x1000 { 274 | Ok(()) 275 | } else { 276 | Err(VfioError::IommuDmaMap) 277 | } 278 | } 279 | 280 | pub(crate) fn unmap_dma( 281 | _container: &VfioContainer, 282 | dma_map: &mut vfio_iommu_type1_dma_unmap, 283 | ) -> Result<()> { 284 | if dma_map.iova == 0x1000 { 285 | Ok(()) 286 | } else { 287 | Err(VfioError::IommuDmaUnmap) 288 | } 289 | } 290 | 291 | pub(crate) fn get_group_status( 292 | _file: &File, 293 | group_status: &mut vfio_group_status, 294 | ) -> Result<()> { 295 | group_status.flags = VFIO_GROUP_FLAGS_VIABLE; 296 | Ok(()) 297 | } 298 | 299 | pub(crate) fn get_group_device_fd(_group: &VfioGroup, _path: &CStr) -> Result { 300 | let tmp_file = TempFile::new().unwrap(); 301 | let device = File::open(tmp_file.as_path()).unwrap(); 302 | 303 | Ok(device) 304 | } 305 | 306 | pub(crate) fn set_group_container(group: &VfioGroup, container: &VfioContainer) -> Result<()> { 307 | if group.as_raw_fd() >= 0 && container.as_raw_fd() >= 0 { 308 | Ok(()) 309 | } else { 310 | Err(VfioError::GroupSetContainer) 311 | } 312 | } 313 | 314 | pub(crate) fn unset_group_container( 315 | group: &VfioGroup, 316 | container: &VfioContainer, 317 | ) -> Result<()> { 318 | if group.as_raw_fd() >= 0 && container.as_raw_fd() >= 0 { 319 | Ok(()) 320 | } else { 321 | Err(VfioError::GroupSetContainer) 322 | } 323 | } 324 | 325 | pub(crate) fn get_device_info(_file: &File, dev_info: &mut vfio_device_info) -> Result<()> { 326 | dev_info.flags = VFIO_DEVICE_FLAGS_PCI; 327 | dev_info.num_regions = VFIO_PCI_CONFIG_REGION_INDEX + 1; 328 | dev_info.num_irqs = VFIO_PCI_MSIX_IRQ_INDEX + 1; 329 | Ok(()) 330 | } 331 | 332 | #[allow(clippy::if_same_then_else)] 333 | pub(crate) fn set_device_irqs(_device: &VfioDevice, irq_sets: &[vfio_irq_set]) -> Result<()> { 334 | if irq_sets.is_empty() 335 | || irq_sets[0].argsz as usize > irq_sets.len() * size_of::() 336 | { 337 | Err(VfioError::VfioDeviceSetIrq) 338 | } else { 339 | let irq_set = &irq_sets[0]; 340 | if irq_set.flags == VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER 341 | && irq_set.index == 0 342 | && irq_set.count == 0 343 | { 344 | Err(VfioError::VfioDeviceSetIrq) 345 | } else if irq_set.flags == VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER 346 | && irq_set.index == 0 347 | && irq_set.count == 0 348 | { 349 | Err(VfioError::VfioDeviceSetIrq) 350 | } else if irq_set.flags == VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK 351 | && irq_set.index == 1 352 | && irq_set.count == 1 353 | { 354 | Err(VfioError::VfioDeviceSetIrq) 355 | } else { 356 | Ok(()) 357 | } 358 | } 359 | } 360 | 361 | pub(crate) fn reset(_device: &VfioDevice) -> i32 { 362 | 0 363 | } 364 | 365 | pub(crate) fn get_device_region_info( 366 | _dev_info: &VfioDeviceInfo, 367 | reg_info: &mut vfio_region_info, 368 | ) -> Result<()> { 369 | match reg_info.index { 370 | 0 => { 371 | reg_info.flags = 0; 372 | reg_info.size = 0x1000; 373 | reg_info.offset = 0x10000; 374 | } 375 | 1 => { 376 | reg_info.argsz = 88; 377 | reg_info.flags = VFIO_REGION_INFO_FLAG_CAPS; 378 | reg_info.size = 0x2000; 379 | reg_info.offset = 0x20000; 380 | } 381 | idx if (2..7).contains(&idx) => { 382 | reg_info.flags = 0; 383 | reg_info.size = (idx as u64 + 1) * 0x1000; 384 | reg_info.offset = (idx as u64 + 1) * 0x10000; 385 | } 386 | 7 => { 387 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 388 | libc::EINVAL, 389 | ))) 390 | } 391 | _ => panic!("invalid device region index"), 392 | } 393 | 394 | Ok(()) 395 | } 396 | 397 | pub(crate) fn get_device_region_info_cap( 398 | _dev_info: &VfioDeviceInfo, 399 | reg_infos: &mut [vfio_region_info_with_cap], 400 | ) -> Result<()> { 401 | if reg_infos.is_empty() 402 | || reg_infos[0].region_info.argsz as usize 403 | > reg_infos.len() * size_of::() 404 | { 405 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 406 | libc::EINVAL, 407 | ))); 408 | } 409 | 410 | let reg_info = &mut reg_infos[0]; 411 | match reg_info.region_info.index { 412 | 1 => { 413 | reg_info.region_info.cap_offset = 32; 414 | let header = unsafe { 415 | &mut *((reg_info as *mut vfio_region_info_with_cap as *mut u8).add(32) 416 | as *mut vfio_info_cap_header) 417 | }; 418 | header.id = VFIO_REGION_INFO_CAP_MSIX_MAPPABLE as u16; 419 | header.next = 40; 420 | 421 | let header = unsafe { 422 | &mut *((header as *mut vfio_info_cap_header as *mut u8).add(8) 423 | as *mut vfio_region_info_cap_type) 424 | }; 425 | header.header.id = VFIO_REGION_INFO_CAP_TYPE as u16; 426 | header.header.next = 56; 427 | header.type_ = 0x5; 428 | header.subtype = 0x6; 429 | 430 | let header = unsafe { 431 | &mut *((header as *mut vfio_region_info_cap_type as *mut u8).add(16) 432 | as *mut vfio_region_info_cap_sparse_mmap) 433 | }; 434 | header.header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP as u16; 435 | header.header.next = 4; 436 | header.nr_areas = 1; 437 | 438 | let mmap = unsafe { 439 | &mut *((header as *mut vfio_region_info_cap_sparse_mmap as *mut u8).add(16) 440 | as *mut vfio_region_sparse_mmap_area) 441 | }; 442 | mmap.size = 0x3; 443 | mmap.offset = 0x4; 444 | } 445 | _ => panic!("invalid device region index"), 446 | } 447 | 448 | Ok(()) 449 | } 450 | 451 | pub(crate) fn get_device_irq_info( 452 | _dev_info: &VfioDeviceInfo, 453 | irq_info: &mut vfio_irq_info, 454 | ) -> Result<()> { 455 | match irq_info.index { 456 | 0 => { 457 | irq_info.flags = VFIO_IRQ_INFO_MASKABLE; 458 | irq_info.count = 1; 459 | } 460 | 1 => { 461 | irq_info.flags = VFIO_IRQ_INFO_EVENTFD; 462 | irq_info.count = 32; 463 | } 464 | 2 => { 465 | irq_info.flags = VFIO_IRQ_INFO_EVENTFD; 466 | irq_info.count = 2048; 467 | } 468 | 3 => { 469 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 470 | libc::EINVAL, 471 | ))) 472 | } 473 | _ => panic!("invalid device irq index"), 474 | } 475 | 476 | Ok(()) 477 | } 478 | 479 | pub(crate) fn create_dev_info_for_test() -> vfio_device_info { 480 | vfio_device_info { 481 | argsz: 0, 482 | flags: 0, 483 | num_regions: 2, 484 | num_irqs: 4, 485 | } 486 | } 487 | } 488 | 489 | #[cfg(test)] 490 | mod tests { 491 | use super::*; 492 | 493 | #[test] 494 | fn test_vfio_ioctl_code() { 495 | assert_eq!(VFIO_GET_API_VERSION(), 15204); 496 | assert_eq!(VFIO_CHECK_EXTENSION(), 15205); 497 | assert_eq!(VFIO_SET_IOMMU(), 15206); 498 | assert_eq!(VFIO_GROUP_GET_STATUS(), 15207); 499 | assert_eq!(VFIO_GROUP_SET_CONTAINER(), 15208); 500 | assert_eq!(VFIO_GROUP_UNSET_CONTAINER(), 15209); 501 | assert_eq!(VFIO_GROUP_GET_DEVICE_FD(), 15210); 502 | assert_eq!(VFIO_DEVICE_GET_INFO(), 15211); 503 | assert_eq!(VFIO_DEVICE_GET_REGION_INFO(), 15212); 504 | assert_eq!(VFIO_DEVICE_GET_IRQ_INFO(), 15213); 505 | assert_eq!(VFIO_DEVICE_SET_IRQS(), 15214); 506 | assert_eq!(VFIO_DEVICE_RESET(), 15215); 507 | assert_eq!(VFIO_DEVICE_IOEVENTFD(), 15220); 508 | assert_eq!(VFIO_IOMMU_DISABLE(), 15220); 509 | } 510 | } 511 | --------------------------------------------------------------------------------