├── .gitignore ├── .gitmodules ├── coverage_config_aarch64.json ├── coverage_config_x86_64.json ├── .cargo └── config.toml ├── CODEOWNERS ├── .github ├── dependabot.yml └── workflows │ ├── dco.yaml │ ├── taplo.yaml │ └── quality.yaml ├── Cargo.toml ├── vfio-bindings ├── src │ ├── vfio_bindings │ │ └── mod.rs │ ├── lib.rs │ └── fam_wrappers.rs ├── README.md ├── Cargo.toml ├── CONTRIBUTING.md ├── LICENSE-BSD ├── CHANGELOG.md └── LICENSE-APACHE ├── vfio-user ├── CHANGELOG.md ├── README.md ├── Cargo.toml ├── LICENSE-BSD-3-Clause ├── examples │ └── gpio │ │ ├── main.rs │ │ └── pci.rs ├── LICENSE-APACHE └── src │ └── lib.rs ├── .buildkite └── custom-tests.json ├── vfio-ioctls ├── Cargo.toml ├── LICENSE-BSD ├── CHANGELOG.md ├── README.md ├── src │ ├── fam.rs │ ├── lib.rs │ └── vfio_ioctls.rs └── LICENSE-APACHE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .idea 3 | .vscode 4 | /target 5 | Cargo.lock 6 | kcov_output 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rust-vmm-ci"] 2 | path = rust-vmm-ci 3 | url = https://github.com/rust-vmm/rust-vmm-ci.git 4 | -------------------------------------------------------------------------------- /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": 51.85, 3 | "exclude_path": "", 4 | "crate_features": "" 5 | } 6 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-musl] 2 | rustflags = ["-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc"] 3 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Add the list of code owners here (using their GitHub username) 2 | * @jiangliu @jinankjain @likebreath @liuw @rbradford @russell-islam @sameo @sboeuf 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 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["vfio-bindings", "vfio-ioctls", "vfio-user"] 3 | resolver = "2" 4 | 5 | [workspace.dependencies] 6 | thiserror = "2.0.16" 7 | vmm-sys-util = "0.15.0" 8 | -------------------------------------------------------------------------------- /vfio-bindings/src/vfio_bindings/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 | #[allow(clippy::undocumented_unsafe_blocks)] 10 | pub mod vfio; 11 | -------------------------------------------------------------------------------- /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 | mod vfio_bindings; 12 | 13 | pub mod bindings { 14 | pub use super::vfio_bindings::*; 15 | 16 | #[cfg(feature = "fam-wrappers")] 17 | pub use super::fam_wrappers::*; 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/dco.yaml: -------------------------------------------------------------------------------- 1 | name: DCO 2 | on: 3 | pull_request: 4 | jobs: 5 | check: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: Set up Python 3.x 10 | uses: actions/setup-python@v1 11 | with: 12 | python-version: '3.x' 13 | - name: Check DCO 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | run: | 17 | pip3 install -U dco-check 18 | dco-check -e "49699333+dependabot[bot]@users.noreply.github.com" 19 | -------------------------------------------------------------------------------- /vfio-user/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Upcoming release 2 | 3 | ## Changed 4 | 5 | ## Added 6 | 7 | ## Fixed 8 | 9 | # [v0.1.2] 10 | 11 | ### Changed 12 | - [[114]](https://github.com/rust-vmm/vfio/pull/114) Cargo.toml: Update deps to latest version 13 | 14 | # [v0.1.1] 15 | 16 | ## Changed 17 | - Bumped vfio-bindings to 0.6.0 18 | 19 | # [v0.1.0] 20 | 21 | This is the first `vfio-user` crate release. 22 | 23 | This crate provides the client and server support for implementing vfio-user devices. 24 | 25 | `vfio-user` is now merged into a single monorepo `vfio`. 26 | -------------------------------------------------------------------------------- /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 | Currently the bindings are generated from Linux kernel version v6.6.0. 9 | 10 | ## Usage 11 | 12 | First, add the following to your Cargo.toml: 13 | ```toml 14 | vfio-bindings = "0.5" 15 | ``` 16 | 17 | Next, to use this bindings, you can do: 18 | ```rust 19 | use vfio_bindings::bindings::vfio::*; 20 | ``` 21 | 22 | ## License 23 | 24 | This code is licensed under Apache-2.0 or BSD-3-Clause. 25 | -------------------------------------------------------------------------------- /vfio-bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfio-bindings" 3 | version = "0.6.1" 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 | [package.metadata.docs.rs] 13 | features = ["fam-wrappers"] 14 | 15 | [features] 16 | vfio-v5_0_0 = [] 17 | fam-wrappers = ["vmm-sys-util"] 18 | 19 | [dependencies] 20 | vmm-sys-util = { workspace = true, optional = true } 21 | 22 | [dev-dependencies] 23 | byteorder = "1.2.1" 24 | -------------------------------------------------------------------------------- /.github/workflows/taplo.yaml: -------------------------------------------------------------------------------- 1 | name: Cargo.toml Formatting (taplo) 2 | on: 3 | pull_request: 4 | paths: 5 | - '**/Cargo.toml' 6 | 7 | jobs: 8 | cargo_toml_format: 9 | name: Cargo.toml Formatting 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Code checkout 13 | uses: actions/checkout@v4 14 | - name: Install Rust toolchain 15 | uses: dtolnay/rust-toolchain@stable 16 | - name: Install build dependencies 17 | run: sudo apt-get update && sudo apt-get -yqq install build-essential libssl-dev 18 | - name: Install taplo 19 | run: cargo install taplo-cli --locked 20 | - name: Check formatting 21 | run: taplo fmt --check 22 | 23 | -------------------------------------------------------------------------------- /vfio-user/README.md: -------------------------------------------------------------------------------- 1 | # vfio_user 2 | 3 | ## Design 4 | 5 | This crate provides the client and server support for implementing vfio-user devices. More details of vfio-user can be found in the [protocol specification](https://github.com/nutanix/libvfio-user/blob/master/docs/vfio-user.rst). 6 | 7 | ## Usage 8 | 9 | There are two structs: 10 | 11 | * `Client` provides a vfio-user client (the part that sits in the VMM) 12 | * `Server` provides a vfio-user server (the part that implements the device) 13 | 14 | ## Examples 15 | 16 | The examples directory contains a sample PCI device implementing a GPIO controller. It can be compiled with `cargo build --examples` 17 | 18 | ## Licence 19 | 20 | This crate is licensed under the Apache 2.0 licence. The full text can be found 21 | in the LICENSE-APACHE file. 22 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /vfio-user/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfio_user" 3 | version = "0.1.2" 4 | authors = ["The Cloud Hypervisor Authors"] 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | description = "Support for vfio-user devices" 8 | repository = "https://github.com/rust-vmm/vfio-user" 9 | 10 | [[example]] 11 | name = "gpio" 12 | 13 | [dependencies] 14 | bitflags = "2.9.4" 15 | libc = "0.2.139" 16 | log = "0.4.17" 17 | serde = { version = "1.0.151", features = ["rc"] } 18 | serde_derive = "1.0.149" 19 | serde_json = "1.0.93" 20 | thiserror = { workspace = true } 21 | vfio-bindings = { version = "=0.6.1", path = "../vfio-bindings", features = [ 22 | "fam-wrappers", 23 | ] } 24 | vm-memory = { version = "0.16.0", features = [ 25 | "backend-mmap", 26 | "backend-atomic", 27 | ] } 28 | vmm-sys-util = { workspace = true } 29 | 30 | [dev-dependencies] 31 | argh = "0.1.9" 32 | env_logger = "0.11.8" 33 | -------------------------------------------------------------------------------- /vfio-ioctls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vfio-ioctls" 3 | version = "0.5.2" 4 | authors = [ 5 | "The Cloud Hypervisor Authors", 6 | "Liu Jiang ", 7 | ] 8 | license = "Apache-2.0 OR BSD-3-Clause" 9 | description = "Safe wrappers over VFIO ioctls" 10 | repository = "https://github.com/rust-vmm/vfio" 11 | readme = "README.md" 12 | edition = "2018" 13 | keywords = ["vfio"] 14 | 15 | [package.metadata.docs.rs] 16 | features = ["kvm"] 17 | 18 | [features] 19 | default = ["kvm"] 20 | kvm = ["kvm-ioctls", "kvm-bindings"] 21 | mshv = ["mshv-ioctls", "mshv-bindings"] 22 | 23 | [dependencies] 24 | byteorder = "1.2.1" 25 | libc = "0.2.39" 26 | log = "0.4" 27 | kvm-bindings = { version = "0.14.0", optional = true } 28 | kvm-ioctls = { version = "0.24.0", optional = true } 29 | thiserror = { workspace = true } 30 | vfio-bindings = { version = "=0.6.1", path = "../vfio-bindings" } 31 | vm-memory = { version = "0.16.0", features = ["backend-mmap"] } 32 | vmm-sys-util = { workspace = true } 33 | mshv-bindings = { version = "0.6.5", features = [ 34 | "with-serde", 35 | "fam-wrappers", 36 | ], optional = true } 37 | mshv-ioctls = { version = "0.6.5", optional = true } 38 | -------------------------------------------------------------------------------- /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) version 0.71.1: 8 | 9 | ```bash 10 | cargo install bindgen-cli --vers 0.71.1 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 | # linux is the repository that you cloned previously. 28 | cd linux 29 | 30 | # Step 1: Checkout the version you want to generate the bindings for. 31 | git checkout v5.2 32 | 33 | # Step 2: Generate the bindings from the kernel headers. 34 | make headers_install INSTALL_HDR_PATH=vfio_headers 35 | cd vfio_headers 36 | bindgen include/linux/vfio.h -o vfio.rs \ 37 | --impl-debug --with-derive-default \ 38 | --with-derive-partialeq --impl-partialeq \ 39 | -- -Iinclude 40 | 41 | cd ~ 42 | 43 | # Step 3: Copy the generated files to the new version module. 44 | cp linux/vfio_headers/vfio.rs vfio-bindings/src/vfio_bindings 45 | ``` 46 | -------------------------------------------------------------------------------- /.github/workflows/quality.yaml: -------------------------------------------------------------------------------- 1 | name: Quality Checks 2 | on: [pull_request, create] 3 | 4 | jobs: 5 | build: 6 | name: Quality (clippy, rustfmt) 7 | runs-on: ubuntu-latest 8 | continue-on-error: ${{ matrix.experimental }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | rust: 13 | - stable 14 | target: 15 | - x86_64-unknown-linux-gnu 16 | - x86_64-unknown-linux-musl 17 | experimental: [false] 18 | include: 19 | - rust: beta 20 | target: x86_64-unknown-linux-gnu 21 | experimental: true 22 | - rust: beta 23 | target: x86_64-unknown-linux-musl 24 | experimental: true 25 | steps: 26 | - name: Code checkout 27 | uses: actions/checkout@v3 28 | with: 29 | fetch-depth: 0 30 | 31 | - name: Install Rust toolchain (${{ matrix.rust }}) 32 | uses: actions-rs/toolchain@v1 33 | with: 34 | toolchain: ${{ matrix.rust }} 35 | target: ${{ matrix.target }} 36 | override: true 37 | components: rustfmt, clippy 38 | 39 | - name: Formatting (rustfmt) 40 | run: cargo fmt -- --check 41 | 42 | - name: Clippy 43 | uses: actions-rs/cargo@v1 44 | with: 45 | use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} 46 | command: clippy 47 | args: --target=${{ matrix.target }} --all --all-targets --tests --examples -- -D warnings -D clippy::undocumented_unsafe_blocks 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vfio-bindings/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Upcoming Release 2 | 3 | ## Changed 4 | 5 | ## Fixed 6 | 7 | ## Added 8 | 9 | # [v0.6.1] 10 | 11 | ## Changed 12 | 13 | - [[114]](https://github.com/rust-vmm/vfio/pull/114) Cargo.toml: Update deps to latest version 14 | 15 | # [v0.6.0] 16 | 17 | ## Changed 18 | 19 | - [[101]](https://github.com/rust-vmm/vfio/pull/101) Disable multi-version support 20 | - [[104]](https://github.com/rust-vmm/vfio/pull/104) Regenerate vfio-bindings with the Linux kernel v6.6.0 21 | 22 | # [v0.5.0] 23 | 24 | ## Fixed 25 | 26 | - [[85]](https://github.com/rust-vmm/vfio/pull/85) Fix file permissions 27 | 28 | ## Changed 29 | 30 | - [[86]](https://github.com/rust-vmm/vfio/pull/86) Upgrade vmm sys utils to v0.14.0 31 | - [[91]](https://github.com/rust-vmm/vfio/pull/91) vfio-bindings: Regenerate bindings using new bindgen-cli 32 | 33 | # [v0.4.0] 34 | 35 | ## Added 36 | 37 | - Update vmm-sys-util to ">=0.12.1" 38 | 39 | # [v0.3.1] 40 | 41 | - Update repository to https://github.com/rust-vmm/vfio 42 | 43 | # [v0.3.0] 44 | 45 | ## Added 46 | 47 | - Update vmm-sys-util version to ">=0.8.0" 48 | 49 | # [v0.2.0] 50 | 51 | ## Added 52 | 53 | - Add FAM wrappers for vfio\_irq\_set 54 | - Update vmm-sys-util version to ">=0.2.0" 55 | 56 | # [v0.1.0] 57 | 58 | This is the first `vfio-bindings` crate release. 59 | 60 | This crate provides Rust FFI bindings to the 61 | [Virtual Function I/O (VFIO)](https://www.kernel.org/doc/Documentation/vfio.txt) 62 | Linux kernel API. With this first release, the bindings are for the Linux kernel 63 | version 5.0. 64 | 65 | The bindings are generated using [bindgen](https://crates.io/crates/bindgen). 66 | -------------------------------------------------------------------------------- /vfio-user/LICENSE-BSD-3-Clause: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The ChromiumOS Authors 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vfio-ioctls/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Upcoming release 2 | 3 | ## Changed 4 | 5 | ## Added 6 | 7 | ## Fixed 8 | 9 | # [v0.5.2] 10 | 11 | ## Changed 12 | 13 | - [[114]](https://github.com/rust-vmm/vfio/pull/114) Cargo.toml: Update deps to latest version 14 | - [[123]] (https://github.com/rust-vmm/vfio/pull/123) Upgrade mshv-bindings and mshv-ioctls to 0.6.5 15 | 16 | ## Added 17 | 18 | ## Fixed 19 | 20 | # [v0.5.1] 21 | 22 | ### Changed 23 | 24 | - [[111]](https://github.com/rust-vmm/vfio/pull/111) vfio-ioctls: upgrade mshv-bindings and mshv-ioctls 25 | 26 | # [v0.5.0] 27 | 28 | ## Changed 29 | 30 | - [[86]](https://github.com/rust-vmm/vfio/pull/86) Upgrade vmm sys utils to v0.14.0 31 | - [[87]](https://github.com/rust-vmm/vfio/pull/87) vfio-ioctls: Upgrade kvm-ioctl & kvm-bindings crates 32 | - [[88]](https://github.com/rust-vmm/vfio/pull/88) Bump thiserror to latest version 33 | 34 | # [v0.4.0] 35 | 36 | ## Added 37 | 38 | - Enable support for Microsoft Hyper-V. 39 | - `VfioError` now propagates the underlying error for more error 40 | types. 41 | - Many structs now derive `Eq` where it makes sense. 42 | - Added `VfioDevice::set_irq_resample_fd` to unmask level-triggered 43 | IRQs via an eventfd. 44 | 45 | ## Changed 46 | 47 | - We skipped to version 0.4.0 to harmonize versions with 48 | `vfio-bindings`. 49 | - The device handle in `VfioContainer::new` has become optional. 50 | - Device file descriptors have their own type (`VfioDeviceFd`) to hide 51 | the underlying hypervisor-specific types. 52 | - Fixed file descriptor handling on big endian architectures. 53 | - Avoid logging errors for querying VGA regions for devices that don't 54 | have them. 55 | 56 | # [v0.1.0] 57 | 58 | This is the first `vfio-ioctl` crate release. 59 | 60 | This crate provides higher-level abstractions for the 61 | [Virtual Function I/O (VFIO)](https://www.kernel.org/doc/Documentation/vfio.txt) 62 | Linux kernel API. 63 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.div_ceil(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 | -------------------------------------------------------------------------------- /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](./vfio-bindings): a rust FFI bindings to VFIO generated using [bindgen](https://crates.io/crates/bindgen). 23 | - [vfio-ioctls](./vfio-ioctls): a group of safe wrappers over the [VFIO APIs](https://github.com/torvalds/linux/blob/master/include/uapi/linux/vfio.h). 24 | - [vfio-user](./vfio-user): a group of safe wrappers to implement [vfio-user devices](https://github.com/nutanix/libvfio-user/blob/master/docs/vfio-user.rst). 25 | 26 | ## License 27 | 28 | This code is licensed under Apache-2.0 or BSD-3-Clause. 29 | -------------------------------------------------------------------------------- /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.div_ceil(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 | // SAFETY: Safe as we create the irq_set_wrapper with the constructor 68 | let irq_set_fam = unsafe { irq_set_wrapper.as_mut_fam_struct() }; 69 | 70 | let fds_fam = irq_set_fam.as_mut_slice(); 71 | for (index, event_fd) in event_fds.iter().enumerate() { 72 | let fds_offset = index * mem::size_of::(); 73 | let fd = &mut fds_fam[fds_offset..fds_offset + mem::size_of::()]; 74 | LittleEndian::write_u32(fd, *event_fd); 75 | } 76 | 77 | irq_set_fam.argsz = mem::size_of::() as u32 78 | + (event_fds.len() * mem::size_of::()) as u32; 79 | irq_set_fam.flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 80 | irq_set_fam.index = 1; 81 | irq_set_fam.start = 0; 82 | irq_set_fam.count = event_fds.len() as u32; 83 | 84 | // Build the same vfio_irq_set structure with the vec_with_array routines 85 | let mut irq_set_vec = vec_with_array_field::(event_fds.len()); 86 | irq_set_vec[0].argsz = mem::size_of::() as u32 87 | + (event_fds.len() * mem::size_of::()) as u32; 88 | irq_set_vec[0].flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; 89 | irq_set_vec[0].index = 1; 90 | irq_set_vec[0].start = 0; 91 | irq_set_vec[0].count = event_fds.len() as u32; 92 | 93 | // SAFETY: irq_set_vec is a valid flexible array constructed by us. 94 | let fds_vec = unsafe { 95 | irq_set_vec[0] 96 | .data 97 | .as_mut_slice(event_fds.len() * mem::size_of::()) 98 | }; 99 | for (index, event_fd) in event_fds.iter().enumerate() { 100 | let fds_offset = index * mem::size_of::(); 101 | let fd = &mut fds_vec[fds_offset..fds_offset + mem::size_of::()]; 102 | LittleEndian::write_u32(fd, *event_fd); 103 | } 104 | 105 | // Both sets should be identical. 106 | assert_eq!( 107 | irq_set_vec 108 | .iter() 109 | .zip(irq_set_wrapper.into_raw().iter()) 110 | .filter(|&(a, b)| a == b) 111 | .count(), 112 | irq_set_vec.len() 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /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, VfioDeviceFd, VfioGroup, VfioIrq, VfioOps, VfioRegion, 70 | VfioRegionInfoCap, VfioRegionInfoCapNvlink2Lnkspd, VfioRegionInfoCapNvlink2Ssatgt, 71 | VfioRegionInfoCapSparseMmap, 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: {0}")] 99 | ContainerSetIOMMU(#[source] SysError), 100 | #[error("failed to get vfio device fd: {0}")] 101 | GroupGetDeviceFD(#[source] SysError), 102 | #[error("failed to set vfio device's attribute: {0}")] 103 | SetDeviceAttr(#[source] SysError), 104 | #[error("failed to get vfio device's info: {0}")] 105 | VfioDeviceGetInfo(#[source] SysError), 106 | #[error("vfio PCI device info doesn't match")] 107 | VfioDeviceGetInfoPCI, 108 | #[error("unsupported vfio device type")] 109 | VfioDeviceGetInfoOther, 110 | #[error("failed to get vfio device's region info: {0}")] 111 | VfioDeviceGetRegionInfo(#[source] SysError), 112 | #[error("invalid file path")] 113 | InvalidPath, 114 | #[error("failed to add guest memory map into iommu table: {0}")] 115 | IommuDmaMap(#[source] SysError), 116 | #[error("failed to remove guest memory map from iommu table: {0}")] 117 | IommuDmaUnmap(#[source] SysError), 118 | #[error("failed to get vfio device irq info")] 119 | VfioDeviceGetIrqInfo, 120 | #[error("failed to set vfio device irq")] 121 | VfioDeviceSetIrq, 122 | #[error("failed to enable vfio device irq")] 123 | VfioDeviceEnableIrq, 124 | #[error("failed to disable vfio device irq")] 125 | VfioDeviceDisableIrq, 126 | #[error("failed to unmask vfio device irq")] 127 | VfioDeviceUnmaskIrq, 128 | #[error("failed to trigger vfio device irq")] 129 | VfioDeviceTriggerIrq, 130 | #[error("failed to set vfio device irq resample fd")] 131 | VfioDeviceSetIrqResampleFd, 132 | #[error("failed to duplicate fd")] 133 | VfioDeviceDupFd, 134 | #[error("wrong device fd type")] 135 | VfioDeviceFdWrongType, 136 | #[error("failed to get host address")] 137 | GetHostAddress, 138 | #[error("invalid dma unmap size")] 139 | InvalidDmaUnmapSize, 140 | #[error("failed to downcast VfioOps")] 141 | DowncastVfioOps, 142 | } 143 | 144 | /// Specialized version of `Result` for VFIO subsystem. 145 | pub type Result = std::result::Result; 146 | 147 | #[cfg(test)] 148 | mod tests { 149 | use super::*; 150 | use std::error::Error; 151 | 152 | #[test] 153 | fn test_vfio_error_fmt() { 154 | let e = VfioError::GetGroupStatus; 155 | let e2 = VfioError::OpenContainer(std::io::Error::from(std::io::ErrorKind::Other)); 156 | let str = format!("{e}"); 157 | 158 | assert_eq!(&str, "failed to get Group Status"); 159 | assert!(e2.source().is_some()); 160 | assert!(e.source().is_none()); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /vfio-user/examples/gpio/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | use argh::FromArgs; 7 | use log::info; 8 | use pci::{PciBarConfiguration, PciSubclass}; 9 | use std::{fs::File, io::Write, mem::size_of, num::Wrapping, path::PathBuf}; 10 | use vfio_bindings::bindings::vfio::{ 11 | vfio_region_info, VFIO_IRQ_INFO_EVENTFD, VFIO_IRQ_SET_ACTION_TRIGGER, 12 | VFIO_IRQ_SET_DATA_EVENTFD, VFIO_PCI_BAR2_REGION_INDEX, VFIO_PCI_CONFIG_REGION_INDEX, 13 | VFIO_PCI_INTX_IRQ_INDEX, VFIO_PCI_NUM_IRQS, VFIO_PCI_NUM_REGIONS, VFIO_REGION_INFO_FLAG_READ, 14 | VFIO_REGION_INFO_FLAG_WRITE, 15 | }; 16 | use vfio_user::{IrqInfo, Server, ServerBackend}; 17 | 18 | mod pci; 19 | 20 | #[derive(Copy, Clone)] 21 | enum PciVfioUserSubclass { 22 | VfioUserSubclass = 0xff, 23 | } 24 | 25 | impl PciSubclass for PciVfioUserSubclass { 26 | fn get_register_value(&self) -> u8 { 27 | *self as u8 28 | } 29 | } 30 | 31 | #[derive(FromArgs)] 32 | /// GPIO test device 33 | struct Args { 34 | /// path to socket 35 | #[argh(option)] 36 | socket_path: String, 37 | } 38 | 39 | struct TestBackend { 40 | configuration: pci::PciConfiguration, 41 | irq: Option, 42 | count: Wrapping, 43 | } 44 | 45 | impl TestBackend { 46 | fn new() -> TestBackend { 47 | let subclass = PciVfioUserSubclass::VfioUserSubclass; 48 | 49 | let mut configuration = pci::PciConfiguration::new( 50 | 0x494f, 51 | 0xdc8, 52 | pci::PciClassCode::Other, 53 | &subclass as &dyn pci::PciSubclass, 54 | None, 55 | pci::PciHeaderType::Device, 56 | 0, 57 | 0, 58 | 0, 59 | ); 60 | 61 | configuration 62 | .add_pci_bar(PciBarConfiguration::new( 63 | VFIO_PCI_BAR2_REGION_INDEX as usize, 64 | 0x100, 65 | pci::PciBarRegionType::IoRegion, 66 | pci::PciBarPrefetchable::NotPrefetchable, 67 | )) 68 | .unwrap(); 69 | 70 | configuration.set_irq(1, pci::PciInterruptPin::IntA); 71 | TestBackend { 72 | configuration, 73 | irq: None, 74 | count: Wrapping(0), 75 | } 76 | } 77 | } 78 | 79 | impl ServerBackend for TestBackend { 80 | fn region_read( 81 | &mut self, 82 | region: u32, 83 | offset: u64, 84 | data: &mut [u8], 85 | ) -> Result<(), std::io::Error> { 86 | let len = data.len(); 87 | info!("read region = {region} offset = {offset} len = {len}"); 88 | 89 | if region == VFIO_PCI_CONFIG_REGION_INDEX { 90 | if len > 4 { 91 | // For larger accesses require multiple of 4 and natural 92 | assert!(len.is_multiple_of(4)); 93 | assert!(offset.is_multiple_of(4)); 94 | let mut reg_idx = offset as usize / 4; 95 | let mut data_offset = 0; 96 | while data_offset < len { 97 | let v = self.configuration.read_reg(reg_idx); 98 | data[data_offset..data_offset + 4].copy_from_slice(&v.to_le_bytes()); 99 | reg_idx += 1; 100 | data_offset += 4; 101 | } 102 | } else { 103 | // Require access to be naturally aligned 104 | let reg_idx = offset as usize / 4; 105 | let reg_offset = offset as usize % 4; 106 | match reg_offset { 107 | 0 => {} 108 | 1 | 3 => assert!(len == 1), 109 | 2 => assert!(len == 1 || len == 2), 110 | _ => unreachable!(), 111 | } 112 | let v = self.configuration.read_reg(reg_idx); 113 | data.copy_from_slice(&v.to_le_bytes()[reg_offset..reg_offset + data.len()]); 114 | } 115 | } else if region == VFIO_PCI_BAR2_REGION_INDEX && offset == 0 { 116 | info!("gpio value read: count = {}", self.count); 117 | self.count += 1; 118 | if self.count.0.is_multiple_of(3) { 119 | data[0] = 1; 120 | if let Some(irq) = &mut self.irq { 121 | info!("Triggering interrupt for count = {}", self.count); 122 | irq.write_all(&1u64.to_le_bytes()).unwrap(); 123 | } 124 | } 125 | } 126 | 127 | Ok(()) 128 | } 129 | 130 | fn region_write( 131 | &mut self, 132 | region: u32, 133 | offset: u64, 134 | data: &[u8], 135 | ) -> Result<(), std::io::Error> { 136 | info!("write region = {region} offset = {offset}"); 137 | if region == VFIO_PCI_CONFIG_REGION_INDEX { 138 | self.configuration 139 | .write_reg(offset as usize / 4, offset % 4, data); 140 | } 141 | 142 | Ok(()) 143 | } 144 | 145 | fn dma_map( 146 | &mut self, 147 | flags: vfio_user::DmaMapFlags, 148 | offset: u64, 149 | address: u64, 150 | size: u64, 151 | fd: Option, 152 | ) -> Result<(), std::io::Error> { 153 | info!("dma_map flags = {flags:?} offset = {offset} address = {address} size = {size} fd = {fd:?}"); 154 | Ok(()) 155 | } 156 | 157 | fn dma_unmap( 158 | &mut self, 159 | flags: vfio_user::DmaUnmapFlags, 160 | address: u64, 161 | size: u64, 162 | ) -> Result<(), std::io::Error> { 163 | info!("dma_unmap flags = {flags:?} address = {address} size = {size}"); 164 | Ok(()) 165 | } 166 | 167 | fn reset(&mut self) -> Result<(), std::io::Error> { 168 | info!("reset"); 169 | Ok(()) 170 | } 171 | 172 | fn set_irqs( 173 | &mut self, 174 | index: u32, 175 | flags: u32, 176 | start: u32, 177 | count: u32, 178 | fds: Vec, 179 | ) -> Result<(), std::io::Error> { 180 | info!("set_irqs index = {index} flags = {flags} start = {start} count = {count} fds = {fds:?}"); 181 | if flags & (VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER) > 0 { 182 | if count == 1 { 183 | self.irq = Some(fds[0].try_clone().unwrap()); 184 | } else { 185 | self.irq = None; 186 | } 187 | } 188 | Ok(()) 189 | } 190 | } 191 | 192 | fn create_regions() -> Vec { 193 | let mut regions = Vec::with_capacity(VFIO_PCI_NUM_REGIONS as usize); 194 | for index in 0..VFIO_PCI_NUM_REGIONS { 195 | let mut region = vfio_region_info { 196 | argsz: size_of::() as u32, 197 | index, 198 | ..Default::default() 199 | }; 200 | 201 | if index == VFIO_PCI_CONFIG_REGION_INDEX || index == VFIO_PCI_BAR2_REGION_INDEX { 202 | region.size = 256; 203 | region.flags = VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE; 204 | } 205 | 206 | regions.push(region); 207 | } 208 | 209 | regions 210 | } 211 | 212 | fn create_irqs() -> Vec { 213 | let mut irqs = Vec::with_capacity(VFIO_PCI_NUM_IRQS as usize); 214 | for index in 0..VFIO_PCI_NUM_IRQS { 215 | let mut irq = IrqInfo { 216 | index, 217 | count: 0, 218 | flags: 0, 219 | }; 220 | 221 | if index == VFIO_PCI_INTX_IRQ_INDEX { 222 | irq.count = 1; 223 | irq.flags = VFIO_IRQ_INFO_EVENTFD 224 | } 225 | 226 | irqs.push(irq); 227 | } 228 | 229 | irqs 230 | } 231 | 232 | fn main() { 233 | let a: Args = argh::from_env(); 234 | env_logger::init(); 235 | let regions = create_regions(); 236 | let irqs = create_irqs(); 237 | 238 | let path = PathBuf::from(a.socket_path); 239 | let s = Server::new(&path, true, irqs, regions).unwrap(); 240 | let mut test_backend = TestBackend::new(); 241 | s.run(&mut test_backend).unwrap(); 242 | } 243 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vfio-user/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.into(), VFIO_BASE); 21 | ioctl_io_nr!(VFIO_CHECK_EXTENSION, VFIO_TYPE.into(), VFIO_BASE + 1); 22 | ioctl_io_nr!(VFIO_SET_IOMMU, VFIO_TYPE.into(), VFIO_BASE + 2); 23 | ioctl_io_nr!(VFIO_GROUP_GET_STATUS, VFIO_TYPE.into(), VFIO_BASE + 3); 24 | ioctl_io_nr!(VFIO_GROUP_SET_CONTAINER, VFIO_TYPE.into(), VFIO_BASE + 4); 25 | ioctl_io_nr!(VFIO_GROUP_UNSET_CONTAINER, VFIO_TYPE.into(), VFIO_BASE + 5); 26 | ioctl_io_nr!(VFIO_GROUP_GET_DEVICE_FD, VFIO_TYPE.into(), VFIO_BASE + 6); 27 | ioctl_io_nr!(VFIO_DEVICE_GET_INFO, VFIO_TYPE.into(), VFIO_BASE + 7); 28 | ioctl_io_nr!(VFIO_DEVICE_GET_REGION_INFO, VFIO_TYPE.into(), VFIO_BASE + 8); 29 | ioctl_io_nr!(VFIO_DEVICE_GET_IRQ_INFO, VFIO_TYPE.into(), VFIO_BASE + 9); 30 | ioctl_io_nr!(VFIO_DEVICE_SET_IRQS, VFIO_TYPE.into(), VFIO_BASE + 10); 31 | ioctl_io_nr!(VFIO_DEVICE_RESET, VFIO_TYPE.into(), VFIO_BASE + 11); 32 | ioctl_io_nr!( 33 | VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, 34 | VFIO_TYPE.into(), 35 | VFIO_BASE + 12 36 | ); 37 | ioctl_io_nr!(VFIO_DEVICE_PCI_HOT_RESET, VFIO_TYPE.into(), VFIO_BASE + 13); 38 | ioctl_io_nr!( 39 | VFIO_DEVICE_QUERY_GFX_PLANE, 40 | VFIO_TYPE.into(), 41 | VFIO_BASE + 14 42 | ); 43 | ioctl_io_nr!(VFIO_DEVICE_GET_GFX_DMABUF, VFIO_TYPE.into(), VFIO_BASE + 15); 44 | ioctl_io_nr!(VFIO_DEVICE_IOEVENTFD, VFIO_TYPE.into(), VFIO_BASE + 16); 45 | ioctl_io_nr!(VFIO_IOMMU_GET_INFO, VFIO_TYPE.into(), VFIO_BASE + 12); 46 | ioctl_io_nr!(VFIO_IOMMU_MAP_DMA, VFIO_TYPE.into(), VFIO_BASE + 13); 47 | ioctl_io_nr!(VFIO_IOMMU_UNMAP_DMA, VFIO_TYPE.into(), VFIO_BASE + 14); 48 | ioctl_io_nr!(VFIO_IOMMU_ENABLE, VFIO_TYPE.into(), VFIO_BASE + 15); 49 | ioctl_io_nr!(VFIO_IOMMU_DISABLE, VFIO_TYPE.into(), VFIO_BASE + 16); 50 | 51 | #[cfg(not(test))] 52 | // Safety: 53 | // - absolutely trust the underlying kernel 54 | // - absolutely trust data returned by the underlying kernel 55 | // - assume kernel will return error if caller passes in invalid file handle, parameter or buffer. 56 | pub(crate) mod vfio_syscall { 57 | use super::*; 58 | use std::os::unix::io::FromRawFd; 59 | use vmm_sys_util::ioctl::{ 60 | ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, 61 | }; 62 | 63 | pub(crate) fn check_api_version(container: &VfioContainer) -> i32 { 64 | // SAFETY: file is vfio container fd and ioctl is defined by kernel. 65 | unsafe { ioctl(container, VFIO_GET_API_VERSION()) } 66 | } 67 | 68 | pub(crate) fn check_extension(container: &VfioContainer, val: u32) -> Result { 69 | // SAFETY: file is vfio container and make sure val is valid. 70 | let ret = unsafe { ioctl_with_val(container, VFIO_CHECK_EXTENSION(), val.into()) }; 71 | if ret < 0 { 72 | Err(VfioError::VfioExtension) 73 | } else { 74 | Ok(ret as u32) 75 | } 76 | } 77 | 78 | pub(crate) fn set_iommu(container: &VfioContainer, val: u32) -> Result<()> { 79 | // SAFETY: file is vfio container and make sure val is valid. 80 | let ret = unsafe { ioctl_with_val(container, VFIO_SET_IOMMU(), val.into()) }; 81 | if ret < 0 { 82 | Err(VfioError::ContainerSetIOMMU(SysError::last())) 83 | } else { 84 | Ok(()) 85 | } 86 | } 87 | 88 | pub(crate) fn map_dma( 89 | container: &VfioContainer, 90 | dma_map: &vfio_iommu_type1_dma_map, 91 | ) -> Result<()> { 92 | // SAFETY: file is vfio container, dma_map is constructed by us, and 93 | // we check the return value 94 | let ret = unsafe { ioctl_with_ref(container, VFIO_IOMMU_MAP_DMA(), dma_map) }; 95 | if ret != 0 { 96 | Err(VfioError::IommuDmaMap(SysError::last())) 97 | } else { 98 | Ok(()) 99 | } 100 | } 101 | 102 | pub(crate) fn unmap_dma( 103 | container: &VfioContainer, 104 | dma_map: &mut vfio_iommu_type1_dma_unmap, 105 | ) -> Result<()> { 106 | // SAFETY: file is vfio container, dma_unmap is constructed by us, and 107 | // we check the return value 108 | let ret = unsafe { ioctl_with_ref(container, VFIO_IOMMU_UNMAP_DMA(), dma_map) }; 109 | if ret != 0 { 110 | Err(VfioError::IommuDmaUnmap(SysError::last())) 111 | } else { 112 | Ok(()) 113 | } 114 | } 115 | 116 | pub(crate) fn get_group_status( 117 | file: &File, 118 | group_status: &mut vfio_group_status, 119 | ) -> Result<()> { 120 | // SAFETY: we are the owner of group and group_status which are valid value. 121 | let ret = unsafe { ioctl_with_mut_ref(file, VFIO_GROUP_GET_STATUS(), group_status) }; 122 | if ret < 0 { 123 | Err(VfioError::GetGroupStatus) 124 | } else { 125 | Ok(()) 126 | } 127 | } 128 | 129 | pub(crate) fn get_group_device_fd(group: &VfioGroup, path: &CStr) -> Result { 130 | // SAFETY: we are the owner of self and path_ptr which are valid value. 131 | let fd = unsafe { ioctl_with_ptr(group, VFIO_GROUP_GET_DEVICE_FD(), path.as_ptr()) }; 132 | if fd < 0 { 133 | Err(VfioError::GroupGetDeviceFD(SysError::last())) 134 | } else { 135 | // SAFETY: fd is valid FD 136 | Ok(unsafe { File::from_raw_fd(fd) }) 137 | } 138 | } 139 | 140 | pub(crate) fn set_group_container(group: &VfioGroup, container: &VfioContainer) -> Result<()> { 141 | let container_raw_fd = container.as_raw_fd(); 142 | // SAFETY: we are the owner of group and container_raw_fd which are valid value, 143 | // and we verify the ret value 144 | let ret = unsafe { ioctl_with_ref(group, VFIO_GROUP_SET_CONTAINER(), &container_raw_fd) }; 145 | if ret < 0 { 146 | Err(VfioError::GroupSetContainer) 147 | } else { 148 | Ok(()) 149 | } 150 | } 151 | 152 | pub(crate) fn unset_group_container( 153 | group: &VfioGroup, 154 | container: &VfioContainer, 155 | ) -> Result<()> { 156 | let container_raw_fd = container.as_raw_fd(); 157 | // SAFETY: we are the owner of self and container_raw_fd which are valid value. 158 | let ret = unsafe { ioctl_with_ref(group, VFIO_GROUP_UNSET_CONTAINER(), &container_raw_fd) }; 159 | if ret < 0 { 160 | Err(VfioError::GroupSetContainer) 161 | } else { 162 | Ok(()) 163 | } 164 | } 165 | 166 | pub(crate) fn get_device_info(file: &File, dev_info: &mut vfio_device_info) -> Result<()> { 167 | // SAFETY: we are the owner of dev and dev_info which are valid value, 168 | // and we verify the return value. 169 | let ret = unsafe { ioctl_with_mut_ref(file, VFIO_DEVICE_GET_INFO(), dev_info) }; 170 | if ret < 0 { 171 | Err(VfioError::VfioDeviceGetInfo(SysError::last())) 172 | } else { 173 | Ok(()) 174 | } 175 | } 176 | 177 | pub(crate) fn set_device_irqs(device: &VfioDevice, irq_set: &[vfio_irq_set]) -> Result<()> { 178 | if irq_set.is_empty() || irq_set[0].argsz as usize > std::mem::size_of_val(irq_set) { 179 | Err(VfioError::VfioDeviceSetIrq) 180 | } else { 181 | // SAFETY: we are the owner of self and irq_set which are valid value 182 | let ret = unsafe { ioctl_with_ref(device, VFIO_DEVICE_SET_IRQS(), &irq_set[0]) }; 183 | if ret < 0 { 184 | Err(VfioError::VfioDeviceSetIrq) 185 | } else { 186 | Ok(()) 187 | } 188 | } 189 | } 190 | 191 | pub(crate) fn reset(device: &VfioDevice) -> i32 { 192 | // SAFETY: file is vfio device 193 | unsafe { ioctl(device, VFIO_DEVICE_RESET()) } 194 | } 195 | 196 | pub(crate) fn get_device_irq_info( 197 | dev_info: &VfioDeviceInfo, 198 | irq_info: &mut vfio_irq_info, 199 | ) -> Result<()> { 200 | // SAFETY: we are the owner of dev and irq_info which are valid value 201 | let ret = unsafe { ioctl_with_mut_ref(dev_info, VFIO_DEVICE_GET_IRQ_INFO(), irq_info) }; 202 | if ret < 0 { 203 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::last())) 204 | } else { 205 | Ok(()) 206 | } 207 | } 208 | 209 | pub(crate) fn get_device_region_info( 210 | dev_info: &VfioDeviceInfo, 211 | reg_info: &mut vfio_region_info, 212 | ) -> Result<()> { 213 | // SAFETY: we are the owner of dev and region_info which are valid value 214 | // and we verify the return value. 215 | let ret = unsafe { ioctl_with_mut_ref(dev_info, VFIO_DEVICE_GET_REGION_INFO(), reg_info) }; 216 | if ret < 0 { 217 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::last())) 218 | } else { 219 | Ok(()) 220 | } 221 | } 222 | 223 | pub(crate) fn get_device_region_info_cap( 224 | dev_info: &VfioDeviceInfo, 225 | reg_infos: &mut [vfio_region_info_with_cap], 226 | ) -> Result<()> { 227 | if reg_infos.is_empty() 228 | || reg_infos[0].region_info.argsz as usize 229 | > reg_infos.len() * size_of::() 230 | { 231 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 232 | libc::EINVAL, 233 | ))) 234 | } else { 235 | // SAFETY: we are the owner of dev and region_info which are valid value, 236 | // and we verify the return value. 237 | let ret = unsafe { 238 | ioctl_with_mut_ref(dev_info, VFIO_DEVICE_GET_REGION_INFO(), &mut reg_infos[0]) 239 | }; 240 | if ret < 0 { 241 | Err(VfioError::VfioDeviceGetRegionInfo(SysError::last())) 242 | } else { 243 | Ok(()) 244 | } 245 | } 246 | } 247 | } 248 | 249 | #[cfg(test)] 250 | pub(crate) mod vfio_syscall { 251 | use super::*; 252 | use vfio_bindings::bindings::vfio::{vfio_device_info, VFIO_IRQ_INFO_EVENTFD}; 253 | use vmm_sys_util::tempfile::TempFile; 254 | 255 | pub(crate) fn check_api_version(_container: &VfioContainer) -> i32 { 256 | VFIO_API_VERSION as i32 257 | } 258 | 259 | pub(crate) fn check_extension(_container: &VfioContainer, val: u32) -> Result { 260 | if val == VFIO_TYPE1v2_IOMMU { 261 | Ok(1) 262 | } else { 263 | Err(VfioError::VfioExtension) 264 | } 265 | } 266 | 267 | pub(crate) fn set_iommu(_container: &VfioContainer, _val: u32) -> Result<()> { 268 | Ok(()) 269 | } 270 | 271 | pub(crate) fn map_dma( 272 | _container: &VfioContainer, 273 | dma_map: &vfio_iommu_type1_dma_map, 274 | ) -> Result<()> { 275 | if dma_map.iova == 0x1000 { 276 | Ok(()) 277 | } else { 278 | Err(VfioError::IommuDmaMap(SysError::last())) 279 | } 280 | } 281 | 282 | pub(crate) fn unmap_dma( 283 | _container: &VfioContainer, 284 | dma_map: &mut vfio_iommu_type1_dma_unmap, 285 | ) -> Result<()> { 286 | if dma_map.iova == 0x1000 { 287 | if dma_map.size == 0x2000 { 288 | dma_map.size = 0x1000; 289 | } 290 | Ok(()) 291 | } else { 292 | Err(VfioError::IommuDmaUnmap(SysError::last())) 293 | } 294 | } 295 | 296 | pub(crate) fn get_group_status( 297 | _file: &File, 298 | group_status: &mut vfio_group_status, 299 | ) -> Result<()> { 300 | group_status.flags = VFIO_GROUP_FLAGS_VIABLE; 301 | Ok(()) 302 | } 303 | 304 | pub(crate) fn get_group_device_fd(_group: &VfioGroup, _path: &CStr) -> Result { 305 | let tmp_file = TempFile::new().unwrap(); 306 | let device = File::open(tmp_file.as_path()).unwrap(); 307 | 308 | Ok(device) 309 | } 310 | 311 | pub(crate) fn set_group_container(group: &VfioGroup, container: &VfioContainer) -> Result<()> { 312 | if group.as_raw_fd() >= 0 && container.as_raw_fd() >= 0 { 313 | Ok(()) 314 | } else { 315 | Err(VfioError::GroupSetContainer) 316 | } 317 | } 318 | 319 | pub(crate) fn unset_group_container( 320 | group: &VfioGroup, 321 | container: &VfioContainer, 322 | ) -> Result<()> { 323 | if group.as_raw_fd() >= 0 && container.as_raw_fd() >= 0 { 324 | Ok(()) 325 | } else { 326 | Err(VfioError::GroupSetContainer) 327 | } 328 | } 329 | 330 | pub(crate) fn get_device_info(_file: &File, dev_info: &mut vfio_device_info) -> Result<()> { 331 | dev_info.flags = VFIO_DEVICE_FLAGS_PCI; 332 | dev_info.num_regions = VFIO_PCI_NUM_REGIONS; 333 | dev_info.num_irqs = VFIO_PCI_MSIX_IRQ_INDEX + 1; 334 | Ok(()) 335 | } 336 | 337 | #[allow(clippy::if_same_then_else)] 338 | pub(crate) fn set_device_irqs(_device: &VfioDevice, irq_sets: &[vfio_irq_set]) -> Result<()> { 339 | if irq_sets.is_empty() || irq_sets[0].argsz as usize > std::mem::size_of_val(irq_sets) { 340 | Err(VfioError::VfioDeviceSetIrq) 341 | } else { 342 | let irq_set = &irq_sets[0]; 343 | if irq_set.flags == VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER 344 | && irq_set.index == 0 345 | && irq_set.count == 0 346 | { 347 | Err(VfioError::VfioDeviceSetIrq) 348 | } else if irq_set.flags == VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER 349 | && irq_set.index == 0 350 | && irq_set.count == 0 351 | { 352 | Err(VfioError::VfioDeviceSetIrq) 353 | } else if irq_set.flags == VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK 354 | && irq_set.index == 1 355 | && irq_set.count == 1 356 | { 357 | Err(VfioError::VfioDeviceSetIrq) 358 | } else { 359 | Ok(()) 360 | } 361 | } 362 | } 363 | 364 | pub(crate) fn reset(_device: &VfioDevice) -> i32 { 365 | 0 366 | } 367 | 368 | pub(crate) fn get_device_region_info( 369 | _dev_info: &VfioDeviceInfo, 370 | reg_info: &mut vfio_region_info, 371 | ) -> Result<()> { 372 | match reg_info.index { 373 | 0 => { 374 | reg_info.flags = 0; 375 | reg_info.size = 0x1000; 376 | reg_info.offset = 0x10000; 377 | } 378 | 1 => { 379 | reg_info.argsz = 88; 380 | reg_info.flags = VFIO_REGION_INFO_FLAG_CAPS; 381 | reg_info.size = 0x2000; 382 | reg_info.offset = 0x20000; 383 | } 384 | idx if idx == VFIO_PCI_VGA_REGION_INDEX => { 385 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 386 | libc::EINVAL, 387 | ))) 388 | } 389 | idx if (2..VFIO_PCI_NUM_REGIONS).contains(&idx) => { 390 | reg_info.flags = 0; 391 | reg_info.size = (idx as u64 + 1) * 0x1000; 392 | reg_info.offset = (idx as u64 + 1) * 0x10000; 393 | } 394 | idx if idx == VFIO_PCI_NUM_REGIONS => { 395 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 396 | libc::EINVAL, 397 | ))) 398 | } 399 | _ => panic!("invalid device region index"), 400 | } 401 | 402 | Ok(()) 403 | } 404 | 405 | pub(crate) fn get_device_region_info_cap( 406 | _dev_info: &VfioDeviceInfo, 407 | reg_infos: &mut [vfio_region_info_with_cap], 408 | ) -> Result<()> { 409 | if reg_infos.is_empty() 410 | || reg_infos[0].region_info.argsz as usize 411 | > reg_infos.len() * size_of::() 412 | { 413 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 414 | libc::EINVAL, 415 | ))); 416 | } 417 | 418 | let reg_info = &mut reg_infos[0]; 419 | match reg_info.region_info.index { 420 | 1 => { 421 | reg_info.region_info.cap_offset = 32; 422 | // SAFETY: data structure returned by kernel is trusted. 423 | let header = unsafe { 424 | &mut *((reg_info as *mut vfio_region_info_with_cap as *mut u8).add(32) 425 | as *mut vfio_info_cap_header) 426 | }; 427 | header.id = VFIO_REGION_INFO_CAP_MSIX_MAPPABLE as u16; 428 | header.next = 40; 429 | 430 | // SAFETY: data structure returned by kernel is trusted. 431 | let header = unsafe { 432 | &mut *((header as *mut vfio_info_cap_header as *mut u8).add(8) 433 | as *mut vfio_region_info_cap_type) 434 | }; 435 | header.header.id = VFIO_REGION_INFO_CAP_TYPE as u16; 436 | header.header.next = 56; 437 | header.type_ = 0x5; 438 | header.subtype = 0x6; 439 | 440 | // SAFETY: data structure returned by kernel is trusted. 441 | let header = unsafe { 442 | &mut *((header as *mut vfio_region_info_cap_type as *mut u8).add(16) 443 | as *mut vfio_region_info_cap_sparse_mmap) 444 | }; 445 | header.header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP as u16; 446 | header.header.next = 4; 447 | header.nr_areas = 1; 448 | 449 | // SAFETY: data structure returned by kernel is trusted. 450 | let mmap = unsafe { 451 | &mut *((header as *mut vfio_region_info_cap_sparse_mmap as *mut u8).add(16) 452 | as *mut vfio_region_sparse_mmap_area) 453 | }; 454 | mmap.size = 0x3; 455 | mmap.offset = 0x4; 456 | } 457 | _ => panic!("invalid device region index"), 458 | } 459 | 460 | Ok(()) 461 | } 462 | 463 | pub(crate) fn get_device_irq_info( 464 | _dev_info: &VfioDeviceInfo, 465 | irq_info: &mut vfio_irq_info, 466 | ) -> Result<()> { 467 | match irq_info.index { 468 | 0 => { 469 | irq_info.flags = VFIO_IRQ_INFO_MASKABLE; 470 | irq_info.count = 1; 471 | } 472 | 1 => { 473 | irq_info.flags = VFIO_IRQ_INFO_EVENTFD; 474 | irq_info.count = 32; 475 | } 476 | 2 => { 477 | irq_info.flags = VFIO_IRQ_INFO_EVENTFD; 478 | irq_info.count = 2048; 479 | } 480 | 3 => { 481 | return Err(VfioError::VfioDeviceGetRegionInfo(SysError::new( 482 | libc::EINVAL, 483 | ))) 484 | } 485 | _ => panic!("invalid device irq index"), 486 | } 487 | 488 | Ok(()) 489 | } 490 | 491 | pub(crate) fn create_dev_info_for_test() -> vfio_device_info { 492 | vfio_device_info { 493 | argsz: 0, 494 | flags: 0, 495 | num_regions: 2, 496 | num_irqs: 4, 497 | cap_offset: 0, 498 | pad: 0, 499 | } 500 | } 501 | } 502 | 503 | #[cfg(test)] 504 | mod tests { 505 | use super::*; 506 | 507 | #[test] 508 | fn test_vfio_ioctl_code() { 509 | assert_eq!(VFIO_GET_API_VERSION(), 15204); 510 | assert_eq!(VFIO_CHECK_EXTENSION(), 15205); 511 | assert_eq!(VFIO_SET_IOMMU(), 15206); 512 | assert_eq!(VFIO_GROUP_GET_STATUS(), 15207); 513 | assert_eq!(VFIO_GROUP_SET_CONTAINER(), 15208); 514 | assert_eq!(VFIO_GROUP_UNSET_CONTAINER(), 15209); 515 | assert_eq!(VFIO_GROUP_GET_DEVICE_FD(), 15210); 516 | assert_eq!(VFIO_DEVICE_GET_INFO(), 15211); 517 | assert_eq!(VFIO_DEVICE_GET_REGION_INFO(), 15212); 518 | assert_eq!(VFIO_DEVICE_GET_IRQ_INFO(), 15213); 519 | assert_eq!(VFIO_DEVICE_SET_IRQS(), 15214); 520 | assert_eq!(VFIO_DEVICE_RESET(), 15215); 521 | assert_eq!(VFIO_DEVICE_IOEVENTFD(), 15220); 522 | assert_eq!(VFIO_IOMMU_DISABLE(), 15220); 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /vfio-user/examples/gpio/pci.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE-BSD-3-Clause file. 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | 6 | use log::warn; 7 | use std::convert::TryInto; 8 | use thiserror::Error; 9 | 10 | /// PCI has four interrupt pins A->D. 11 | #[derive(Copy, Clone, Ord, PartialOrd, PartialEq, Eq)] 12 | #[allow(dead_code)] 13 | pub enum PciInterruptPin { 14 | IntA, 15 | IntB, 16 | IntC, 17 | IntD, 18 | } 19 | 20 | // The number of 32bit registers in the config space, 256 bytes. 21 | const NUM_CONFIGURATION_REGISTERS: usize = 64; 22 | #[allow(dead_code)] 23 | pub const PCI_ID_REG: usize = 0; 24 | pub const COMMAND_REG: usize = 1; 25 | pub const COMMAND_REG_IO_SPACE_MASK: u32 = 0x0000_0001; 26 | pub const COMMAND_REG_MEMORY_SPACE_MASK: u32 = 0x0000_0002; 27 | #[allow(dead_code)] 28 | const STATUS_REG: usize = 1; 29 | #[allow(dead_code)] 30 | pub const STATUS_REG_CAPABILITIES_USED_MASK: u32 = 0x0010_0000; 31 | #[allow(dead_code)] 32 | pub const CLASS_REG: usize = 2; 33 | #[allow(dead_code)] 34 | pub const HEADER_TYPE_REG: usize = 3; 35 | #[allow(dead_code)] 36 | pub const HEADER_TYPE_MULTIFUNCTION_MASK: u32 = 0x0080_0000; 37 | pub const BAR0_REG: usize = 4; 38 | const BAR_IO_ADDR_MASK: u32 = 0xffff_fffc; 39 | const BAR_IO_MIN_SIZE: u64 = 4; 40 | const BAR_MEM_ADDR_MASK: u32 = 0xffff_fff0; 41 | const BAR_MEM_MIN_SIZE: u64 = 16; 42 | const BAR_ROM_MIN_SIZE: u64 = 2048; 43 | pub const NUM_BAR_REGS: usize = 7; // 6 normal BARs + expansion ROM BAR. 44 | pub const ROM_BAR_IDX: PciBarIndex = 6; 45 | pub const ROM_BAR_REG: usize = 12; 46 | #[allow(dead_code)] 47 | pub const CAPABILITY_LIST_HEAD_OFFSET: usize = 0x34; 48 | #[allow(dead_code)] 49 | pub const PCI_CAP_NEXT_POINTER: usize = 0x1; 50 | #[allow(dead_code)] 51 | const FIRST_CAPABILITY_OFFSET: usize = 0x40; 52 | #[allow(dead_code)] 53 | pub const CAPABILITY_MAX_OFFSET: usize = 255; 54 | 55 | const INTERRUPT_LINE_PIN_REG: usize = 15; 56 | 57 | /// Represents the types of PCI headers allowed in the configuration registers. 58 | #[allow(dead_code)] 59 | #[derive(Copy, Clone)] 60 | pub enum PciHeaderType { 61 | Device, 62 | Bridge, 63 | } 64 | 65 | /// Classes of PCI nodes. 66 | #[allow(dead_code)] 67 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 68 | pub enum PciClassCode { 69 | TooOld, 70 | MassStorage, 71 | NetworkController, 72 | DisplayController, 73 | MultimediaController, 74 | MemoryController, 75 | BridgeDevice, 76 | SimpleCommunicationController, 77 | BaseSystemPeripheral, 78 | InputDevice, 79 | DockingStation, 80 | Processor, 81 | SerialBusController, 82 | WirelessController, 83 | IntelligentIoController, 84 | SatelliteCommunicationController, 85 | EncryptionController, 86 | DataAcquisitionSignalProcessing, 87 | ProcessingAccelerator, 88 | NonEssentialInstrumentation, 89 | Other = 0xff, 90 | } 91 | 92 | impl PciClassCode { 93 | pub fn get_register_value(&self) -> u8 { 94 | *self as u8 95 | } 96 | } 97 | 98 | /// A PCI sublcass. Each class in `PciClassCode` can specify a unique set of subclasses. This trait 99 | /// is implemented by each subclass. It allows use of a trait object to generate configurations. 100 | pub trait PciSubclass { 101 | /// Convert this subclass to the value used in the PCI specification. 102 | fn get_register_value(&self) -> u8; 103 | } 104 | 105 | /// Subclasses of the DisplayController class. 106 | #[allow(dead_code)] 107 | #[derive(Copy, Clone)] 108 | pub enum PciDisplaySubclass { 109 | VgaCompatibleController = 0x00, 110 | XgaCompatibleController = 0x01, 111 | ThreeDController = 0x02, 112 | Other = 0x80, 113 | } 114 | 115 | impl PciSubclass for PciDisplaySubclass { 116 | fn get_register_value(&self) -> u8 { 117 | *self as u8 118 | } 119 | } 120 | 121 | /// Subclasses of the MultimediaController class. 122 | #[allow(dead_code)] 123 | #[derive(Copy, Clone)] 124 | pub enum PciMultimediaSubclass { 125 | VideoController = 0x00, 126 | AudioController = 0x01, 127 | TelephonyDevice = 0x02, 128 | AudioDevice = 0x03, 129 | Other = 0x80, 130 | } 131 | 132 | impl PciSubclass for PciMultimediaSubclass { 133 | fn get_register_value(&self) -> u8 { 134 | *self as u8 135 | } 136 | } 137 | 138 | /// Subclasses of the BridgeDevice 139 | #[allow(dead_code)] 140 | #[derive(Copy, Clone)] 141 | pub enum PciBridgeSubclass { 142 | HostBridge = 0x00, 143 | IsaBridge = 0x01, 144 | EisaBridge = 0x02, 145 | McaBridge = 0x03, 146 | PciToPciBridge = 0x04, 147 | PcmciaBridge = 0x05, 148 | NuBusBridge = 0x06, 149 | CardBusBridge = 0x07, 150 | RaceWayBridge = 0x08, 151 | PciToPciSemiTransparentBridge = 0x09, 152 | InfiniBrandToPciHostBridge = 0x0a, 153 | OtherBridgeDevice = 0x80, 154 | } 155 | 156 | impl PciSubclass for PciBridgeSubclass { 157 | fn get_register_value(&self) -> u8 { 158 | *self as u8 159 | } 160 | } 161 | 162 | /// Subclass of the SerialBus 163 | #[allow(dead_code)] 164 | #[derive(Copy, Clone)] 165 | pub enum PciSerialBusSubClass { 166 | Firewire = 0x00, 167 | AccessBus = 0x01, 168 | Ssa = 0x02, 169 | Usb = 0x03, 170 | } 171 | 172 | impl PciSubclass for PciSerialBusSubClass { 173 | fn get_register_value(&self) -> u8 { 174 | *self as u8 175 | } 176 | } 177 | 178 | /// Subclasses for PciClassCode Other. 179 | #[allow(dead_code)] 180 | #[derive(Copy, Clone)] 181 | #[repr(u8)] 182 | pub enum PciOtherSubclass { 183 | Other = 0xff, 184 | } 185 | 186 | impl PciSubclass for PciOtherSubclass { 187 | fn get_register_value(&self) -> u8 { 188 | *self as u8 189 | } 190 | } 191 | 192 | /// A PCI class programming interface. Each combination of `PciClassCode` and 193 | /// `PciSubclass` can specify a set of register-level programming interfaces. 194 | /// This trait is implemented by each programming interface. 195 | /// It allows use of a trait object to generate configurations. 196 | pub trait PciProgrammingInterface { 197 | /// Convert this programming interface to the value used in the PCI specification. 198 | fn get_register_value(&self) -> u8; 199 | } 200 | 201 | /// Types of PCI capabilities. 202 | #[allow(dead_code)] 203 | pub enum PciCapabilityID { 204 | ListID = 0, 205 | PowerManagement = 0x01, 206 | AcceleratedGraphicsPort = 0x02, 207 | VitalProductData = 0x03, 208 | SlotIdentification = 0x04, 209 | MessageSignalledInterrupts = 0x05, 210 | CompactPciHotSwap = 0x06, 211 | Pcix = 0x07, 212 | HyperTransport = 0x08, 213 | VendorSpecific = 0x09, 214 | Debugport = 0x0A, 215 | CompactPciCentralResourceControl = 0x0B, 216 | PciStandardHotPlugController = 0x0C, 217 | BridgeSubsystemVendorDeviceID = 0x0D, 218 | AgpTargetPciPciBridge = 0x0E, 219 | SecureDevice = 0x0F, 220 | PciExpress = 0x10, 221 | Msix = 0x11, 222 | SataDataIndexConf = 0x12, 223 | PciAdvancedFeatures = 0x13, 224 | PciEnhancedAllocation = 0x14, 225 | } 226 | 227 | /// A PCI capability list. Devices can optionally specify capabilities in their configuration space. 228 | pub trait PciCapability { 229 | fn bytes(&self) -> &[u8]; 230 | fn id(&self) -> PciCapabilityID; 231 | fn writable_bits(&self) -> Vec; 232 | } 233 | 234 | /// Contains the configuration space of a PCI node. 235 | /// See the [specification](https://en.wikipedia.org/wiki/PCI_configuration_space). 236 | /// The configuration space is accessed with DWORD reads and writes from the guest. 237 | pub struct PciConfiguration { 238 | registers: [u32; NUM_CONFIGURATION_REGISTERS], 239 | writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register. 240 | bar_used: [bool; NUM_BAR_REGS], 241 | bar_configs: [Option; NUM_BAR_REGS], 242 | // Contains the byte offset and size of the last capability. 243 | #[allow(dead_code)] 244 | last_capability: Option<(usize, usize)>, 245 | } 246 | 247 | /// See pci_regs.h in kernel 248 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 249 | #[allow(dead_code)] 250 | #[allow(clippy::enum_variant_names)] 251 | pub enum PciBarRegionType { 252 | Memory32BitRegion = 0, 253 | IoRegion = 0x01, 254 | Memory64BitRegion = 0x04, 255 | } 256 | 257 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 258 | #[allow(dead_code)] 259 | pub enum PciBarPrefetchable { 260 | NotPrefetchable = 0, 261 | Prefetchable = 0x08, 262 | } 263 | 264 | pub type PciBarIndex = usize; 265 | 266 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 267 | pub struct PciBarConfiguration { 268 | addr: u64, 269 | size: u64, 270 | bar_idx: PciBarIndex, 271 | region_type: PciBarRegionType, 272 | prefetchable: PciBarPrefetchable, 273 | } 274 | 275 | pub struct PciBarIter<'a> { 276 | config: &'a PciConfiguration, 277 | bar_num: PciBarIndex, 278 | } 279 | 280 | impl Iterator for PciBarIter<'_> { 281 | type Item = PciBarConfiguration; 282 | 283 | fn next(&mut self) -> Option { 284 | while self.bar_num < NUM_BAR_REGS { 285 | let bar_config = self.config.get_bar_configuration(self.bar_num); 286 | self.bar_num += 1; 287 | if let Some(bar_config) = bar_config { 288 | return Some(bar_config); 289 | } 290 | } 291 | 292 | None 293 | } 294 | } 295 | 296 | #[derive(Error, Debug, PartialEq, Eq)] 297 | #[allow(dead_code)] 298 | pub enum Error { 299 | #[error("address {0} size {1} too big")] 300 | BarAddressInvalid(u64, u64), 301 | #[error("address {0} is not aligned to size {1}")] 302 | BarAlignmentInvalid(u64, u64), 303 | #[error("bar {0} already used")] 304 | BarInUse(PciBarIndex), 305 | #[error("64bit bar {0} already used (requires two regs)")] 306 | BarInUse64(PciBarIndex), 307 | #[error("bar {bar_index} invalid, max {max}", bar_index = .0, max = NUM_BAR_REGS - 1)] 308 | BarInvalid(PciBarIndex), 309 | #[error("64bitbar {bar_index} invalid, requires two regs, {max}", bar_index = .0, max = ROM_BAR_IDX - 1)] 310 | BarInvalid64(PciBarIndex), 311 | #[error("expansion rom bar must be a memory region")] 312 | BarInvalidRomType, 313 | #[error("bar address {0} not a power of two")] 314 | BarSizeInvalid(u64), 315 | #[error("empty capabilities are invalid")] 316 | CapabilityEmpty, 317 | #[error("Invalid capability length {0}")] 318 | CapabilityLengthInvalid(usize), 319 | #[error("capability of size {0} doesn't fit")] 320 | CapabilitySpaceFull(usize), 321 | } 322 | 323 | pub type Result = std::result::Result; 324 | 325 | impl PciConfiguration { 326 | #[allow(clippy::too_many_arguments)] 327 | pub fn new( 328 | vendor_id: u16, 329 | device_id: u16, 330 | class_code: PciClassCode, 331 | subclass: &dyn PciSubclass, 332 | programming_interface: Option<&dyn PciProgrammingInterface>, 333 | header_type: PciHeaderType, 334 | subsystem_vendor_id: u16, 335 | subsystem_id: u16, 336 | revision_id: u8, 337 | ) -> Self { 338 | let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS]; 339 | let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS]; 340 | registers[0] = (u32::from(device_id) << 16) | u32::from(vendor_id); 341 | // TODO(dverkamp): Status should be write-1-to-clear 342 | writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w) 343 | let pi = if let Some(pi) = programming_interface { 344 | pi.get_register_value() 345 | } else { 346 | 0 347 | }; 348 | registers[2] = (u32::from(class_code.get_register_value()) << 24) 349 | | (u32::from(subclass.get_register_value()) << 16) 350 | | (u32::from(pi) << 8) 351 | | u32::from(revision_id); 352 | writable_bits[3] = 0x0000_00ff; // Cacheline size (r/w) 353 | match header_type { 354 | PciHeaderType::Device => { 355 | registers[3] = 0x0000_0000; // Header type 0 (device) 356 | writable_bits[15] = 0x0000_00ff; // Interrupt line (r/w) 357 | registers[11] = (u32::from(subsystem_id) << 16) | u32::from(subsystem_vendor_id); 358 | } 359 | PciHeaderType::Bridge => { 360 | registers[3] = 0x0001_0000; // Header type 1 (bridge) 361 | writable_bits[6] = 0x00ff_ffff; // Primary/secondary/subordinate bus number, 362 | // secondary latency timer 363 | registers[7] = 0x0000_00f0; // IO base > IO Limit, no IO address on secondary side at initialize 364 | writable_bits[7] = 0xf900_0000; // IO base and limit, secondary status, 365 | registers[8] = 0x0000_fff0; // mem base > mem Limit, no MMIO address on secondary side at initialize 366 | writable_bits[8] = 0xfff0_fff0; // Memory base and limit 367 | registers[9] = 0x0001_fff1; // pmem base > pmem Limit, no prefetch MMIO address on secondary side at initialize 368 | writable_bits[9] = 0xfff0_fff0; // Prefetchable base and limit 369 | writable_bits[10] = 0xffff_ffff; // Prefetchable base upper 32 bits 370 | writable_bits[11] = 0xffff_ffff; // Prefetchable limit upper 32 bits 371 | writable_bits[15] = 0xffff_00ff; // Bridge control (r/w), interrupt line (r/w) 372 | } 373 | }; 374 | 375 | PciConfiguration { 376 | registers, 377 | writable_bits, 378 | bar_used: [false; NUM_BAR_REGS], 379 | bar_configs: [None; NUM_BAR_REGS], 380 | last_capability: None, 381 | } 382 | } 383 | 384 | /// Reads a 32bit register from `reg_idx` in the register map. 385 | pub fn read_reg(&self, reg_idx: usize) -> u32 { 386 | *(self.registers.get(reg_idx).unwrap_or(&0xffff_ffff)) 387 | } 388 | 389 | /// Writes data to PciConfiguration.registers. 390 | /// `reg_idx` - index into PciConfiguration.registers. 391 | /// `offset` - PciConfiguration.registers is in unit of DWord, offset define byte 392 | /// offset in the DWrod. 393 | /// `data` - The data to write. 394 | pub fn write_reg(&mut self, reg_idx: usize, offset: u64, data: &[u8]) { 395 | let reg_offset = reg_idx * 4 + offset as usize; 396 | match data.len() { 397 | 1 => self.write_byte(reg_offset, data[0]), 398 | 2 => self.write_word(reg_offset, u16::from_le_bytes(data.try_into().unwrap())), 399 | 4 => self.write_dword(reg_offset, u32::from_le_bytes(data.try_into().unwrap())), 400 | _ => (), 401 | } 402 | } 403 | 404 | /// Writes a 32bit dword to `offset`. `offset` must be 32bit aligned. 405 | fn write_dword(&mut self, offset: usize, value: u32) { 406 | if !offset.is_multiple_of(4) { 407 | warn!("bad PCI config dword write offset {offset}"); 408 | return; 409 | } 410 | let reg_idx = offset / 4; 411 | if let Some(r) = self.registers.get_mut(reg_idx) { 412 | *r = (*r & !self.writable_bits[reg_idx]) | (value & self.writable_bits[reg_idx]); 413 | } else { 414 | warn!("bad PCI dword write {offset}"); 415 | } 416 | } 417 | 418 | /// Writes a 16bit word to `offset`. `offset` must be 16bit aligned. 419 | fn write_word(&mut self, offset: usize, value: u16) { 420 | let shift = match offset % 4 { 421 | 0 => 0, 422 | 2 => 16, 423 | _ => { 424 | warn!("bad PCI config word write offset {offset}"); 425 | return; 426 | } 427 | }; 428 | let reg_idx = offset / 4; 429 | 430 | if let Some(r) = self.registers.get_mut(reg_idx) { 431 | let writable_mask = self.writable_bits[reg_idx]; 432 | let mask = (0xffffu32 << shift) & writable_mask; 433 | let shifted_value = (u32::from(value) << shift) & writable_mask; 434 | *r = *r & !mask | shifted_value; 435 | } else { 436 | warn!("bad PCI config word write offset {offset}"); 437 | } 438 | } 439 | 440 | /// Writes a byte to `offset`. 441 | fn write_byte(&mut self, offset: usize, value: u8) { 442 | self.write_byte_internal(offset, value, true); 443 | } 444 | 445 | /// Writes a byte to `offset`, optionally enforcing read-only bits. 446 | fn write_byte_internal(&mut self, offset: usize, value: u8, apply_writable_mask: bool) { 447 | let shift = (offset % 4) * 8; 448 | let reg_idx = offset / 4; 449 | 450 | if let Some(r) = self.registers.get_mut(reg_idx) { 451 | let writable_mask = if apply_writable_mask { 452 | self.writable_bits[reg_idx] 453 | } else { 454 | 0xffff_ffff 455 | }; 456 | let mask = (0xffu32 << shift) & writable_mask; 457 | let shifted_value = (u32::from(value) << shift) & writable_mask; 458 | *r = *r & !mask | shifted_value; 459 | } else { 460 | warn!("bad PCI config byte write offset {offset}"); 461 | } 462 | } 463 | 464 | /// Adds a region specified by `config`. Configures the specified BAR(s) to 465 | /// report this region and size to the guest kernel. Enforces a few constraints 466 | /// (i.e, region size must be power of two, register not already used). Returns 'None' on 467 | /// failure all, `Some(BarIndex)` on success. 468 | pub fn add_pci_bar(&mut self, config: PciBarConfiguration) -> Result { 469 | if config.bar_idx >= NUM_BAR_REGS { 470 | return Err(Error::BarInvalid(config.bar_idx)); 471 | } 472 | 473 | if self.bar_used[config.bar_idx] { 474 | return Err(Error::BarInUse(config.bar_idx)); 475 | } 476 | 477 | if config.size.count_ones() != 1 { 478 | return Err(Error::BarSizeInvalid(config.size)); 479 | } 480 | 481 | if config.is_expansion_rom() && config.region_type != PciBarRegionType::Memory32BitRegion { 482 | return Err(Error::BarInvalidRomType); 483 | } 484 | 485 | let min_size = if config.is_expansion_rom() { 486 | BAR_ROM_MIN_SIZE 487 | } else if config.region_type == PciBarRegionType::IoRegion { 488 | BAR_IO_MIN_SIZE 489 | } else { 490 | BAR_MEM_MIN_SIZE 491 | }; 492 | 493 | if config.size < min_size { 494 | return Err(Error::BarSizeInvalid(config.size)); 495 | } 496 | 497 | if !config.addr.is_multiple_of(config.size) { 498 | return Err(Error::BarAlignmentInvalid(config.addr, config.size)); 499 | } 500 | 501 | let reg_idx = config.reg_index(); 502 | let end_addr = config 503 | .addr 504 | .checked_add(config.size) 505 | .ok_or(Error::BarAddressInvalid(config.addr, config.size))?; 506 | match config.region_type { 507 | PciBarRegionType::Memory32BitRegion | PciBarRegionType::IoRegion => { 508 | if end_addr > u64::from(u32::MAX) { 509 | return Err(Error::BarAddressInvalid(config.addr, config.size)); 510 | } 511 | } 512 | PciBarRegionType::Memory64BitRegion => { 513 | // The expansion ROM BAR cannot be used for part of a 64-bit BAR. 514 | if config.bar_idx + 1 >= ROM_BAR_IDX { 515 | return Err(Error::BarInvalid64(config.bar_idx)); 516 | } 517 | 518 | if end_addr > u64::from(u32::MAX) { 519 | return Err(Error::BarAddressInvalid(config.addr, config.size)); 520 | } 521 | 522 | if self.bar_used[config.bar_idx + 1] { 523 | return Err(Error::BarInUse64(config.bar_idx)); 524 | } 525 | 526 | self.registers[reg_idx + 1] = (config.addr >> 32) as u32; 527 | self.writable_bits[reg_idx + 1] = !((config.size - 1) >> 32) as u32; 528 | self.bar_used[config.bar_idx + 1] = true; 529 | } 530 | } 531 | 532 | let (mask, lower_bits) = match config.region_type { 533 | PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion => { 534 | self.registers[COMMAND_REG] |= COMMAND_REG_MEMORY_SPACE_MASK; 535 | ( 536 | BAR_MEM_ADDR_MASK, 537 | config.prefetchable as u32 | config.region_type as u32, 538 | ) 539 | } 540 | PciBarRegionType::IoRegion => { 541 | self.registers[COMMAND_REG] |= COMMAND_REG_IO_SPACE_MASK; 542 | (BAR_IO_ADDR_MASK, config.region_type as u32) 543 | } 544 | }; 545 | 546 | self.registers[reg_idx] = ((config.addr as u32) & mask) | lower_bits; 547 | self.writable_bits[reg_idx] = !(config.size - 1) as u32; 548 | if config.is_expansion_rom() { 549 | self.writable_bits[reg_idx] |= 1; // Expansion ROM enable bit. 550 | } 551 | self.bar_used[config.bar_idx] = true; 552 | self.bar_configs[config.bar_idx] = Some(config); 553 | Ok(config.bar_idx) 554 | } 555 | 556 | /// Returns an iterator of the currently configured base address registers. 557 | #[allow(dead_code)] // TODO(dverkamp): remove this once used 558 | pub fn get_bars(&self) -> PciBarIter<'_> { 559 | PciBarIter { 560 | config: self, 561 | bar_num: 0, 562 | } 563 | } 564 | 565 | /// Returns the configuration of a base address register, if present. 566 | pub fn get_bar_configuration(&self, bar_num: usize) -> Option { 567 | let config = self.bar_configs.get(bar_num)?; 568 | 569 | if let Some(mut config) = config { 570 | let command = self.read_reg(COMMAND_REG); 571 | if (config.is_memory() && (command & COMMAND_REG_MEMORY_SPACE_MASK == 0)) 572 | || (config.is_io() && (command & COMMAND_REG_IO_SPACE_MASK == 0)) 573 | { 574 | return None; 575 | } 576 | 577 | // The address may have been modified by the guest, so the value in bar_configs 578 | // may be outdated. Replace it with the current value. 579 | config.addr = self.get_bar_addr(bar_num); 580 | Some(config) 581 | } else { 582 | None 583 | } 584 | } 585 | 586 | /// Returns the type of the given BAR region. 587 | pub fn get_bar_type(&self, bar_num: PciBarIndex) -> Option { 588 | self.bar_configs.get(bar_num)?.map(|c| c.region_type) 589 | } 590 | 591 | /// Returns the address of the given BAR region. 592 | pub fn get_bar_addr(&self, bar_num: PciBarIndex) -> u64 { 593 | let bar_idx = if bar_num == ROM_BAR_IDX { 594 | ROM_BAR_REG 595 | } else { 596 | BAR0_REG + bar_num 597 | }; 598 | 599 | let bar_type = match self.get_bar_type(bar_num) { 600 | Some(t) => t, 601 | None => return 0, 602 | }; 603 | 604 | match bar_type { 605 | PciBarRegionType::IoRegion => u64::from(self.registers[bar_idx] & BAR_IO_ADDR_MASK), 606 | PciBarRegionType::Memory32BitRegion => { 607 | u64::from(self.registers[bar_idx] & BAR_MEM_ADDR_MASK) 608 | } 609 | PciBarRegionType::Memory64BitRegion => { 610 | u64::from(self.registers[bar_idx] & BAR_MEM_ADDR_MASK) 611 | | (u64::from(self.registers[bar_idx + 1]) << 32) 612 | } 613 | } 614 | } 615 | 616 | /// Configures the IRQ line and pin used by this device. 617 | pub fn set_irq(&mut self, line: u8, pin: PciInterruptPin) { 618 | // `pin` is 1-based in the pci config space. 619 | let pin_idx = (pin as u32) + 1; 620 | self.registers[INTERRUPT_LINE_PIN_REG] = (self.registers[INTERRUPT_LINE_PIN_REG] 621 | & 0xffff_0000) 622 | | (pin_idx << 8) 623 | | u32::from(line); 624 | } 625 | 626 | /// Adds the capability `cap_data` to the list of capabilities. 627 | /// `cap_data` should include the two-byte PCI capability header (type, next), 628 | /// but not populate it. Correct values will be generated automatically based 629 | /// on `cap_data.id()`. 630 | #[allow(dead_code)] 631 | pub fn add_capability(&mut self, cap_data: &dyn PciCapability) -> Result { 632 | let total_len = cap_data.bytes().len(); 633 | // Check that the length is valid. 634 | if cap_data.bytes().is_empty() { 635 | return Err(Error::CapabilityEmpty); 636 | } 637 | let (cap_offset, tail_offset) = match self.last_capability { 638 | Some((offset, len)) => (Self::next_dword(offset, len), offset + 1), 639 | None => (FIRST_CAPABILITY_OFFSET, CAPABILITY_LIST_HEAD_OFFSET), 640 | }; 641 | let end_offset = cap_offset 642 | .checked_add(total_len) 643 | .ok_or(Error::CapabilitySpaceFull(total_len))?; 644 | if end_offset > CAPABILITY_MAX_OFFSET { 645 | return Err(Error::CapabilitySpaceFull(total_len)); 646 | } 647 | self.registers[STATUS_REG] |= STATUS_REG_CAPABILITIES_USED_MASK; 648 | self.write_byte_internal(tail_offset, cap_offset as u8, false); 649 | self.write_byte_internal(cap_offset, cap_data.id() as u8, false); 650 | self.write_byte_internal(cap_offset + 1, 0, false); // Next pointer. 651 | for (i, byte) in cap_data.bytes().iter().enumerate().skip(2) { 652 | self.write_byte_internal(cap_offset + i, *byte, false); 653 | } 654 | let reg_idx = cap_offset / 4; 655 | for (i, dword) in cap_data.writable_bits().iter().enumerate() { 656 | self.writable_bits[reg_idx + i] = *dword; 657 | } 658 | self.last_capability = Some((cap_offset, total_len)); 659 | Ok(cap_offset) 660 | } 661 | 662 | // Find the next aligned offset after the one given. 663 | fn next_dword(offset: usize, len: usize) -> usize { 664 | let next = offset + len; 665 | (next + 3) & !3 666 | } 667 | } 668 | 669 | impl PciBarConfiguration { 670 | pub fn new( 671 | bar_idx: PciBarIndex, 672 | size: u64, 673 | region_type: PciBarRegionType, 674 | prefetchable: PciBarPrefetchable, 675 | ) -> Self { 676 | PciBarConfiguration { 677 | bar_idx, 678 | addr: 0, 679 | size, 680 | region_type, 681 | prefetchable, 682 | } 683 | } 684 | #[allow(dead_code)] 685 | pub fn bar_index(&self) -> PciBarIndex { 686 | self.bar_idx 687 | } 688 | 689 | pub fn reg_index(&self) -> usize { 690 | if self.bar_idx == ROM_BAR_IDX { 691 | ROM_BAR_REG 692 | } else { 693 | BAR0_REG + self.bar_idx 694 | } 695 | } 696 | #[allow(dead_code)] 697 | pub fn address(&self) -> u64 { 698 | self.addr 699 | } 700 | #[allow(dead_code)] 701 | pub fn address_range(&self) -> std::ops::Range { 702 | self.addr..self.addr + self.size 703 | } 704 | #[allow(dead_code)] 705 | pub fn set_address(mut self, addr: u64) -> Self { 706 | self.addr = addr; 707 | self 708 | } 709 | #[allow(dead_code)] 710 | pub fn size(&self) -> u64 { 711 | self.size 712 | } 713 | 714 | pub fn is_expansion_rom(&self) -> bool { 715 | self.bar_idx == ROM_BAR_IDX 716 | } 717 | 718 | pub fn is_memory(&self) -> bool { 719 | matches!( 720 | self.region_type, 721 | PciBarRegionType::Memory32BitRegion | PciBarRegionType::Memory64BitRegion 722 | ) 723 | } 724 | #[allow(dead_code)] 725 | pub fn is_64bit_memory(&self) -> bool { 726 | self.region_type == PciBarRegionType::Memory64BitRegion 727 | } 728 | 729 | pub fn is_io(&self) -> bool { 730 | self.region_type == PciBarRegionType::IoRegion 731 | } 732 | 733 | #[allow(dead_code)] 734 | pub fn is_prefetchable(&self) -> bool { 735 | self.is_memory() && self.prefetchable == PciBarPrefetchable::Prefetchable 736 | } 737 | } 738 | -------------------------------------------------------------------------------- /vfio-user/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2021 Intel Corporation 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | use bitflags::bitflags; 7 | use libc::{c_void, iovec, EINVAL}; 8 | use libc::{sysconf, _SC_PAGESIZE}; 9 | use std::ffi::CString; 10 | use std::fs::File; 11 | use std::io::{IoSlice, Read, Write}; 12 | use std::mem::size_of; 13 | use std::num::Wrapping; 14 | use std::os::unix::{ 15 | io::{FromRawFd, RawFd}, 16 | net::{UnixListener, UnixStream}, 17 | }; 18 | use std::path::Path; 19 | use thiserror::Error; 20 | use vfio_bindings::bindings::vfio::*; 21 | use vm_memory::{ByteValued, FileOffset}; 22 | use vmm_sys_util::sock_ctrl_msg::ScmSocket; 23 | 24 | #[macro_use] 25 | extern crate serde_derive; 26 | 27 | #[macro_use] 28 | extern crate log; 29 | 30 | #[allow(dead_code)] 31 | #[repr(u16)] 32 | #[derive(Clone, Copy, Debug, Default)] 33 | pub enum Command { 34 | #[default] 35 | Unknown = 0, 36 | Version = 1, 37 | DmaMap = 2, 38 | DmaUnmap = 3, 39 | DeviceGetInfo = 4, 40 | DeviceGetRegionInfo = 5, 41 | GetRegionIoFds = 6, 42 | GetIrqInfo = 7, 43 | SetIrqs = 8, 44 | RegionRead = 9, 45 | RegionWrite = 10, 46 | DmaRead = 11, 47 | DmaWrite = 12, 48 | DeviceReset = 13, 49 | UserDirtyPages = 14, 50 | } 51 | 52 | #[allow(dead_code)] 53 | #[repr(u32)] 54 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] 55 | enum HeaderFlags { 56 | #[default] 57 | Command = 0, 58 | Reply = 1, 59 | NoReply = 1 << 4, 60 | Error = 1 << 5, 61 | } 62 | 63 | #[repr(C)] 64 | #[derive(Default, Clone, Copy, Debug)] 65 | struct Header { 66 | message_id: u16, 67 | command: Command, 68 | message_size: u32, 69 | flags: u32, 70 | error: u32, 71 | } 72 | 73 | #[repr(C)] 74 | #[derive(Default, Clone, Copy, Debug)] 75 | struct Version { 76 | header: Header, 77 | major: u16, 78 | minor: u16, 79 | } 80 | 81 | #[derive(Serialize, Deserialize, Debug)] 82 | struct MigrationCapabilities { 83 | pgsize: u32, 84 | } 85 | 86 | const fn default_max_msg_fds() -> u32 { 87 | 1 88 | } 89 | 90 | const fn default_max_data_xfer_size() -> u32 { 91 | 1048576 92 | } 93 | 94 | #[inline(always)] 95 | fn pagesize() -> u32 { 96 | // SAFETY: sysconf 97 | unsafe { sysconf(_SC_PAGESIZE) as u32 } 98 | } 99 | 100 | fn default_migration_capabilities() -> MigrationCapabilities { 101 | MigrationCapabilities { pgsize: pagesize() } 102 | } 103 | 104 | bitflags! { 105 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 106 | pub struct DmaMapFlags: u32 { 107 | const READ = 1 << 0; 108 | const WRITE = 1 << 1; 109 | const READ_WRITE = Self::READ.bits() | Self::WRITE.bits(); 110 | 111 | // There might be unknown bits and we don't want bitflags to clear them. 112 | const _ = !0; 113 | } 114 | 115 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 116 | pub struct DmaUnmapFlags: u32 { 117 | const GET_DIRTY_PAGE_INFO = 1 << 1; 118 | const UNMAP_ALL = 1 << 2; 119 | 120 | // See above. 121 | const _ = !0; 122 | } 123 | } 124 | 125 | #[repr(C)] 126 | #[derive(Default, Clone, Copy, Debug)] 127 | struct DmaMap { 128 | header: Header, 129 | argsz: u32, 130 | flags: u32, 131 | offset: u64, 132 | address: u64, 133 | size: u64, 134 | } 135 | 136 | #[repr(C)] 137 | #[derive(Default, Clone, Copy, Debug)] 138 | struct DmaUnmap { 139 | header: Header, 140 | argsz: u32, 141 | flags: u32, 142 | address: u64, 143 | size: u64, 144 | } 145 | 146 | #[repr(C)] 147 | #[derive(Default, Clone, Copy, Debug)] 148 | struct DeviceGetInfo { 149 | header: Header, 150 | argsz: u32, 151 | flags: u32, 152 | num_regions: u32, 153 | num_irqs: u32, 154 | } 155 | 156 | #[repr(C)] 157 | #[derive(Default, Clone, Copy, Debug)] 158 | struct DeviceGetRegionInfo { 159 | header: Header, 160 | region_info: vfio_region_info, 161 | } 162 | 163 | #[repr(C)] 164 | #[derive(Default, Clone, Copy, Debug)] 165 | struct RegionAccess { 166 | header: Header, 167 | offset: u64, 168 | region: u32, 169 | count: u32, 170 | } 171 | 172 | #[repr(C)] 173 | #[derive(Default, Clone, Copy, Debug)] 174 | struct GetIrqInfo { 175 | header: Header, 176 | argsz: u32, 177 | flags: u32, 178 | index: u32, 179 | count: u32, 180 | } 181 | 182 | #[repr(C)] 183 | #[derive(Default, Clone, Copy, Debug)] 184 | struct SetIrqs { 185 | header: Header, 186 | argsz: u32, 187 | flags: u32, 188 | index: u32, 189 | start: u32, 190 | count: u32, 191 | } 192 | 193 | #[repr(C)] 194 | #[derive(Default, Clone, Copy, Debug)] 195 | struct DeviceReset { 196 | header: Header, 197 | } 198 | 199 | // SAFETY: data structure only contain a series of integers 200 | unsafe impl ByteValued for Header {} 201 | // SAFETY: data structure only contain a series of integers 202 | unsafe impl ByteValued for Version {} 203 | // SAFETY: data structure only contain a series of integers 204 | unsafe impl ByteValued for DmaMap {} 205 | // SAFETY: data structure only contain a series of integers 206 | unsafe impl ByteValued for DmaUnmap {} 207 | // SAFETY: data structure only contain a series of integers 208 | unsafe impl ByteValued for DeviceGetInfo {} 209 | // SAFETY: data structure only contain a series of integers 210 | unsafe impl ByteValued for DeviceGetRegionInfo {} 211 | // SAFETY: data structure only contain a series of integers 212 | unsafe impl ByteValued for RegionAccess {} 213 | // SAFETY: data structure only contain a series of integers 214 | unsafe impl ByteValued for GetIrqInfo {} 215 | // SAFETY: data structure only contain a series of integers 216 | unsafe impl ByteValued for SetIrqs {} 217 | // SAFETY: data structure only contain a series of integers 218 | unsafe impl ByteValued for DeviceReset {} 219 | 220 | #[derive(Serialize, Deserialize, Debug)] 221 | struct Capabilities { 222 | #[serde(default = "default_max_msg_fds")] 223 | max_msg_fds: u32, 224 | #[serde(default = "default_max_data_xfer_size")] 225 | max_data_xfer_size: u32, 226 | #[serde(default = "default_migration_capabilities")] 227 | migration: MigrationCapabilities, 228 | } 229 | 230 | #[derive(Serialize, Deserialize, Debug, Default)] 231 | struct CapabilitiesData { 232 | capabilities: Capabilities, 233 | } 234 | 235 | impl Default for Capabilities { 236 | fn default() -> Self { 237 | Self { 238 | max_msg_fds: default_max_msg_fds(), 239 | max_data_xfer_size: default_max_data_xfer_size(), 240 | migration: default_migration_capabilities(), 241 | } 242 | } 243 | } 244 | 245 | pub struct Client { 246 | stream: UnixStream, 247 | next_message_id: Wrapping, 248 | num_irqs: u32, 249 | resettable: bool, 250 | regions: Vec, 251 | } 252 | 253 | #[derive(Debug)] 254 | pub struct Region { 255 | pub flags: u32, 256 | pub index: u32, 257 | pub size: u64, 258 | pub file_offset: Option, 259 | pub sparse_areas: Vec, 260 | } 261 | 262 | #[derive(Debug)] 263 | pub struct IrqInfo { 264 | pub index: u32, 265 | pub flags: u32, 266 | pub count: u32, 267 | } 268 | 269 | #[derive(Error, Debug)] 270 | pub enum Error { 271 | #[error("Error connecting: {0}")] 272 | Connect(#[source] std::io::Error), 273 | #[error("Error serializing capabilities: {0}")] 274 | SerializeCapabilites(#[source] serde_json::Error), 275 | #[error("Error deserializing capabilities: {0}")] 276 | DeserializeCapabilites(#[source] serde_json::Error), 277 | #[error("Error writing to stream: {0}")] 278 | StreamWrite(#[source] std::io::Error), 279 | #[error("Error reading from stream: {0}")] 280 | StreamRead(#[source] std::io::Error), 281 | #[error("Error shutting down stream: {0}")] 282 | StreamShutdown(#[source] std::io::Error), 283 | #[error("Error writing with file descriptors: {0}")] 284 | SendWithFd(#[source] vmm_sys_util::errno::Error), 285 | #[error("Error reading with file descriptors: {0}")] 286 | ReceiveWithFd(#[source] vmm_sys_util::errno::Error), 287 | #[error("Not a PCI device")] 288 | NotPciDevice, 289 | #[error("Error binding to socket: {0}")] 290 | SocketBind(#[source] std::io::Error), 291 | #[error("Error accepting connection: {0}")] 292 | SocketAccept(#[source] std::io::Error), 293 | #[error("Unsupported command: {0:?}")] 294 | UnsupportedCommand(Command), 295 | #[error("Unsupported feature")] 296 | UnsupportedFeature, 297 | #[error("Error from backend: {0:?}")] 298 | Backend(#[source] std::io::Error), 299 | #[error("Invalid input")] 300 | InvalidInput, 301 | } 302 | 303 | impl Client { 304 | pub fn new(path: &Path) -> Result { 305 | let stream = UnixStream::connect(path).map_err(Error::Connect)?; 306 | 307 | let mut client = Client { 308 | next_message_id: Wrapping(0), 309 | stream, 310 | num_irqs: 0, 311 | resettable: false, 312 | regions: Vec::new(), 313 | }; 314 | 315 | client.negotiate_version()?; 316 | 317 | client.regions = client.get_regions()?; 318 | 319 | Ok(client) 320 | } 321 | 322 | fn negotiate_version(&mut self) -> Result<(), Error> { 323 | let caps = CapabilitiesData::default(); 324 | 325 | let version_data = serde_json::to_string(&caps).map_err(Error::SerializeCapabilites)?; 326 | 327 | let version = Version { 328 | header: Header { 329 | message_id: self.next_message_id.0, 330 | command: Command::Version, 331 | flags: HeaderFlags::Command as u32, 332 | message_size: (size_of::() + version_data.len() + 1) as u32, 333 | ..Default::default() 334 | }, 335 | major: 0, 336 | minor: 1, 337 | }; 338 | debug!("Command: {version:?}"); 339 | 340 | let version_data = CString::new(version_data.as_bytes()).unwrap(); 341 | let bufs = vec![ 342 | IoSlice::new(version.as_slice()), 343 | IoSlice::new(version_data.as_bytes_with_nul()), 344 | ]; 345 | 346 | // TODO: Use write_all_vectored() when ready 347 | let _ = self 348 | .stream 349 | .write_vectored(&bufs) 350 | .map_err(Error::StreamWrite)?; 351 | 352 | debug!( 353 | "Sent client version information: major = {} minor = {} capabilities = {:?}", 354 | version.major, version.minor, &caps.capabilities 355 | ); 356 | 357 | self.next_message_id += Wrapping(1); 358 | 359 | let mut server_version: Version = Version::default(); 360 | self.stream 361 | .read_exact(server_version.as_mut_slice()) 362 | .map_err(Error::StreamRead)?; 363 | 364 | debug!("Reply: {server_version:?}"); 365 | 366 | let mut server_version_data = 367 | vec![0; server_version.header.message_size as usize - size_of::()]; 368 | self.stream 369 | .read_exact(server_version_data.as_mut_slice()) 370 | .map_err(Error::StreamRead)?; 371 | 372 | let server_caps: CapabilitiesData = 373 | serde_json::from_slice(&server_version_data[0..server_version_data.len() - 1]) 374 | .map_err(Error::DeserializeCapabilites)?; 375 | 376 | debug!( 377 | "Received server version information: major = {} minor = {} capabilities = {:?}", 378 | server_version.major, server_version.minor, &server_caps.capabilities 379 | ); 380 | 381 | Ok(()) 382 | } 383 | 384 | pub fn dma_map( 385 | &mut self, 386 | offset: u64, 387 | address: u64, 388 | size: u64, 389 | fd: RawFd, 390 | ) -> Result<(), Error> { 391 | let dma_map = DmaMap { 392 | header: Header { 393 | message_id: self.next_message_id.0, 394 | command: Command::DmaMap, 395 | flags: HeaderFlags::Command as u32, 396 | message_size: size_of::() as u32, 397 | ..Default::default() 398 | }, 399 | argsz: (size_of::() - size_of::
()) as u32, 400 | flags: DmaMapFlags::READ_WRITE.bits(), 401 | offset, 402 | address, 403 | size, 404 | }; 405 | debug!("Command: {dma_map:?}"); 406 | self.next_message_id += Wrapping(1); 407 | self.stream 408 | .send_with_fd(dma_map.as_slice(), fd) 409 | .map_err(Error::SendWithFd)?; 410 | 411 | let mut reply = Header::default(); 412 | self.stream 413 | .read_exact(reply.as_mut_slice()) 414 | .map_err(Error::StreamRead)?; 415 | debug!("Reply: {reply:?}"); 416 | 417 | Ok(()) 418 | } 419 | 420 | pub fn dma_unmap(&mut self, address: u64, size: u64) -> Result<(), Error> { 421 | let dma_unmap = DmaUnmap { 422 | header: Header { 423 | message_id: self.next_message_id.0, 424 | command: Command::DmaUnmap, 425 | flags: HeaderFlags::Command as u32, 426 | message_size: size_of::() as u32, 427 | ..Default::default() 428 | }, 429 | argsz: (size_of::() - size_of::
()) as u32, 430 | flags: 0, 431 | address, 432 | size, 433 | }; 434 | debug!("Command: {dma_unmap:?}"); 435 | self.next_message_id += Wrapping(1); 436 | self.stream 437 | .write_all(dma_unmap.as_slice()) 438 | .map_err(Error::StreamWrite)?; 439 | 440 | let mut reply = DmaUnmap::default(); 441 | self.stream 442 | .read_exact(reply.as_mut_slice()) 443 | .map_err(Error::StreamRead)?; 444 | debug!("Reply: {reply:?}"); 445 | 446 | Ok(()) 447 | } 448 | 449 | pub fn reset(&mut self) -> Result<(), Error> { 450 | let reset = DeviceReset { 451 | header: Header { 452 | message_id: self.next_message_id.0, 453 | command: Command::DeviceReset, 454 | flags: HeaderFlags::Command as u32, 455 | message_size: size_of::() as u32, 456 | ..Default::default() 457 | }, 458 | }; 459 | debug!("Command: {reset:?}"); 460 | self.next_message_id += Wrapping(1); 461 | self.stream 462 | .write_all(reset.as_slice()) 463 | .map_err(Error::StreamWrite)?; 464 | 465 | let mut reply = Header::default(); 466 | self.stream 467 | .read_exact(reply.as_mut_slice()) 468 | .map_err(Error::StreamRead)?; 469 | debug!("Reply: {reply:?}"); 470 | 471 | Ok(()) 472 | } 473 | 474 | fn get_regions(&mut self) -> Result, Error> { 475 | let get_info = DeviceGetInfo { 476 | header: Header { 477 | message_id: self.next_message_id.0, 478 | command: Command::DeviceGetInfo, 479 | flags: HeaderFlags::Command as u32, 480 | message_size: size_of::() as u32, 481 | ..Default::default() 482 | }, 483 | argsz: size_of::() as u32, 484 | ..Default::default() 485 | }; 486 | debug!("Command: {get_info:?}"); 487 | self.next_message_id += Wrapping(1); 488 | 489 | self.stream 490 | .write_all(get_info.as_slice()) 491 | .map_err(Error::StreamWrite)?; 492 | 493 | let mut reply = DeviceGetInfo::default(); 494 | self.stream 495 | .read_exact(reply.as_mut_slice()) 496 | .map_err(Error::StreamRead)?; 497 | debug!("Reply: {reply:?}"); 498 | self.num_irqs = reply.num_irqs; 499 | 500 | if reply.flags & VFIO_DEVICE_FLAGS_PCI != VFIO_DEVICE_FLAGS_PCI { 501 | return Err(Error::NotPciDevice); 502 | } 503 | 504 | self.resettable = reply.flags & VFIO_DEVICE_FLAGS_RESET != VFIO_DEVICE_FLAGS_RESET; 505 | 506 | let num_regions = reply.num_regions; 507 | let mut regions = Vec::new(); 508 | for index in 0..num_regions { 509 | let (region_info, fd, sparse_areas) = self.get_region_info(index)?; 510 | regions.push(Region { 511 | flags: region_info.flags, 512 | index: region_info.index, 513 | size: region_info.size, 514 | file_offset: fd.map(|fd| FileOffset::new(fd, region_info.offset)), 515 | sparse_areas, 516 | }); 517 | } 518 | 519 | Ok(regions) 520 | } 521 | 522 | fn get_region_info( 523 | &mut self, 524 | index: u32, 525 | ) -> Result< 526 | ( 527 | vfio_region_info, 528 | Option, 529 | Vec, 530 | ), 531 | Error, 532 | > { 533 | // Retrieve the region info without capability 534 | let mut get_region_info = DeviceGetRegionInfo { 535 | header: Header { 536 | message_id: self.next_message_id.0, 537 | command: Command::DeviceGetRegionInfo, 538 | flags: HeaderFlags::Command as u32, 539 | message_size: std::mem::size_of::() as u32, 540 | ..Default::default() 541 | }, 542 | region_info: vfio_region_info { 543 | argsz: size_of::() as u32, 544 | index, 545 | ..Default::default() 546 | }, 547 | }; 548 | debug!("Command: {get_region_info:?}"); 549 | self.next_message_id += Wrapping(1); 550 | 551 | self.stream 552 | .write_all(get_region_info.as_slice()) 553 | .map_err(Error::StreamWrite)?; 554 | 555 | let mut reply = DeviceGetRegionInfo::default(); 556 | let (_, fd) = self 557 | .stream 558 | .recv_with_fd(reply.as_mut_slice()) 559 | .map_err(Error::ReceiveWithFd)?; 560 | debug!("Reply: {reply:?}"); 561 | 562 | // Retrieve the region info again with capabilities if needed 563 | if reply.region_info.argsz > std::mem::size_of::() as u32 { 564 | get_region_info.region_info.argsz = reply.region_info.argsz; 565 | debug!("Command: {get_region_info:?}"); 566 | self.next_message_id += Wrapping(1); 567 | 568 | self.stream 569 | .write_all(get_region_info.as_slice()) 570 | .map_err(Error::StreamWrite)?; 571 | 572 | let mut reply = DeviceGetRegionInfo::default(); 573 | let (_, fd) = self 574 | .stream 575 | .recv_with_fd(reply.as_mut_slice()) 576 | .map_err(Error::ReceiveWithFd)?; 577 | debug!("Reply: {reply:?}"); 578 | 579 | let cap_size = reply.region_info.argsz - std::mem::size_of::() as u32; 580 | assert_eq!( 581 | cap_size, 582 | reply.header.message_size - size_of::() as u32 583 | ); 584 | let mut cap_data = vec![0; cap_size as usize]; 585 | self.stream 586 | .read_exact(cap_data.as_mut_slice()) 587 | .map_err(Error::StreamRead)?; 588 | 589 | let sparse_areas = Self::parse_region_caps(&cap_data, &reply.region_info)?; 590 | 591 | Ok((reply.region_info, fd, sparse_areas)) 592 | } else { 593 | Ok((reply.region_info, fd, Vec::new())) 594 | } 595 | } 596 | 597 | fn parse_region_caps( 598 | cap_data: &[u8], 599 | region_info: &vfio_region_info, 600 | ) -> Result, Error> { 601 | let mut sparse_areas: Vec = Vec::new(); 602 | 603 | let cap_size = cap_data.len() as u32; 604 | let cap_header_size = size_of::() as u32; 605 | let mmap_cap_size = size_of::() as u32; 606 | let mmap_area_size = size_of::() as u32; 607 | 608 | let cap_data_ptr = cap_data.as_ptr(); 609 | let mut region_info_offset = region_info.cap_offset; 610 | while region_info_offset != 0 { 611 | // calculate the offset from the begining of the cap_data based on the offset 612 | // that is relative to the begining of the VFIO region info structure 613 | let cap_offset = region_info_offset - size_of::() as u32; 614 | if cap_offset + cap_header_size > cap_size { 615 | warn!( 616 | "Unexpected end of cap data: 'cap_offset + cap_header_size > cap_size' \ 617 | cap_offset = {cap_offset}, cap_header_size = {cap_header_size}, cap_size = {cap_size}" 618 | ); 619 | break; 620 | } 621 | 622 | // SAFETY: `cap_data_ptr` is valid and the `cap_offset` is checked above 623 | let cap_ptr = unsafe { cap_data_ptr.offset(cap_offset as isize) }; 624 | // SAFETY: `cap_ptr` is valid 625 | let cap_header = unsafe { &*(cap_ptr as *const vfio_info_cap_header) }; 626 | match cap_header.id as u32 { 627 | VFIO_REGION_INFO_CAP_SPARSE_MMAP => { 628 | if cap_offset + mmap_cap_size > cap_size { 629 | warn!( 630 | "Unexpected end of cap data: 'cap_offset + mmap_cap_size > cap_size' \ 631 | cap_offset = {cap_offset}, mmap_cap_size = {mmap_cap_size}, cap_size = {cap_size}" 632 | ); 633 | break; 634 | } 635 | // SAFETY: `cap_ptr` is valid and its size is also checked above 636 | let sparse_mmap = unsafe { 637 | &*(cap_ptr as *mut u8 as *const vfio_region_info_cap_sparse_mmap) 638 | }; 639 | 640 | let area_num = sparse_mmap.nr_areas; 641 | if cap_offset + mmap_cap_size + area_num * mmap_area_size > cap_size { 642 | warn!("Unexpected end of cap data: 'cap_offset + mmap_cap_size + area_num * mmap_area_size > cap_size' \ 643 | cap_offset = {cap_offset}, mmap_cap_size = {mmap_area_size}, area_num = {area_num}, mmap_area_size = {mmap_area_size}, cap_size = {cap_size}"); 644 | break; 645 | } 646 | let areas = 647 | // SAFETY: `sparse_mmap` is valid and its size is also checked above 648 | unsafe { sparse_mmap.areas.as_slice(sparse_mmap.nr_areas as usize) }; 649 | for area in areas.iter() { 650 | sparse_areas.push(*area); 651 | } 652 | } 653 | _ => { 654 | warn!( 655 | "Ignoring unsupported vfio region capability (id = '{}')", 656 | cap_header.id 657 | ); 658 | } 659 | } 660 | region_info_offset = cap_header.next; 661 | } 662 | 663 | Ok(sparse_areas) 664 | } 665 | 666 | pub fn region_read(&mut self, region: u32, offset: u64, data: &mut [u8]) -> Result<(), Error> { 667 | let region_read = RegionAccess { 668 | header: Header { 669 | message_id: self.next_message_id.0, 670 | command: Command::RegionRead, 671 | flags: HeaderFlags::Command as u32, 672 | message_size: size_of::() as u32, 673 | ..Default::default() 674 | }, 675 | offset, 676 | count: data.len() as u32, 677 | region, 678 | }; 679 | debug!("Command: {region_read:?}"); 680 | self.next_message_id += Wrapping(1); 681 | self.stream 682 | .write_all(region_read.as_slice()) 683 | .map_err(Error::StreamWrite)?; 684 | 685 | let mut reply = RegionAccess::default(); 686 | self.stream 687 | .read_exact(reply.as_mut_slice()) 688 | .map_err(Error::StreamRead)?; 689 | debug!("Reply: {reply:?}"); 690 | self.stream.read_exact(data).map_err(Error::StreamRead)?; 691 | Ok(()) 692 | } 693 | 694 | pub fn region_write(&mut self, region: u32, offset: u64, data: &[u8]) -> Result<(), Error> { 695 | let region_write = RegionAccess { 696 | header: Header { 697 | message_id: self.next_message_id.0, 698 | command: Command::RegionWrite, 699 | flags: HeaderFlags::Command as u32, 700 | message_size: (size_of::() + data.len()) as u32, 701 | ..Default::default() 702 | }, 703 | offset, 704 | count: data.len() as u32, 705 | region, 706 | }; 707 | debug!("Command: {region_write:?}"); 708 | self.next_message_id += Wrapping(1); 709 | 710 | let bufs = vec![IoSlice::new(region_write.as_slice()), IoSlice::new(data)]; 711 | 712 | // TODO: Use write_all_vectored() when ready 713 | let _ = self 714 | .stream 715 | .write_vectored(&bufs) 716 | .map_err(Error::StreamWrite)?; 717 | 718 | let mut reply = RegionAccess::default(); 719 | self.stream 720 | .read_exact(reply.as_mut_slice()) 721 | .map_err(Error::StreamRead)?; 722 | debug!("Reply: {reply:?}"); 723 | Ok(()) 724 | } 725 | 726 | pub fn get_irq_info(&mut self, index: u32) -> Result { 727 | let get_irq_info = GetIrqInfo { 728 | header: Header { 729 | message_id: self.next_message_id.0, 730 | command: Command::GetIrqInfo, 731 | flags: HeaderFlags::Command as u32, 732 | message_size: size_of::() as u32, 733 | ..Default::default() 734 | }, 735 | argsz: (size_of::() - size_of::
()) as u32, 736 | flags: 0, 737 | index, 738 | count: 0, 739 | }; 740 | debug!("Command: {get_irq_info:?}"); 741 | self.next_message_id += Wrapping(1); 742 | 743 | self.stream 744 | .write_all(get_irq_info.as_slice()) 745 | .map_err(Error::StreamWrite)?; 746 | 747 | let mut reply = GetIrqInfo::default(); 748 | self.stream 749 | .read_exact(reply.as_mut_slice()) 750 | .map_err(Error::StreamRead)?; 751 | debug!("Reply: {reply:?}"); 752 | 753 | Ok(IrqInfo { 754 | index: reply.index, 755 | flags: reply.flags, 756 | count: reply.count, 757 | }) 758 | } 759 | 760 | pub fn set_irqs( 761 | &mut self, 762 | index: u32, 763 | flags: u32, 764 | start: u32, 765 | count: u32, 766 | fds: &[RawFd], 767 | ) -> Result<(), Error> { 768 | let set_irqs = SetIrqs { 769 | header: Header { 770 | message_id: self.next_message_id.0, 771 | command: Command::SetIrqs, 772 | flags: HeaderFlags::Command as u32, 773 | message_size: size_of::() as u32, 774 | ..Default::default() 775 | }, 776 | argsz: (size_of::() - size_of::
()) as u32, 777 | flags, 778 | start, 779 | index, 780 | count, 781 | }; 782 | debug!("Command: {set_irqs:?}"); 783 | self.next_message_id += Wrapping(1); 784 | 785 | self.stream 786 | .send_with_fds(&[set_irqs.as_slice()], fds) 787 | .map_err(Error::SendWithFd)?; 788 | 789 | let mut reply = Header::default(); 790 | self.stream 791 | .read_exact(reply.as_mut_slice()) 792 | .map_err(Error::StreamRead)?; 793 | debug!("Reply: {reply:?}"); 794 | 795 | Ok(()) 796 | } 797 | 798 | pub fn region(&self, region_index: u32) -> Option<&Region> { 799 | self.regions 800 | .iter() 801 | .find(|®ion| region.index == region_index) 802 | } 803 | 804 | pub fn resettable(&self) -> bool { 805 | self.resettable 806 | } 807 | 808 | pub fn shutdown(&self) -> Result<(), Error> { 809 | self.stream 810 | .shutdown(std::net::Shutdown::Both) 811 | .map_err(Error::StreamShutdown) 812 | } 813 | } 814 | 815 | pub trait ServerBackend { 816 | fn region_read( 817 | &mut self, 818 | _region: u32, 819 | _offset: u64, 820 | _data: &mut [u8], 821 | ) -> Result<(), std::io::Error>; 822 | fn region_write( 823 | &mut self, 824 | _region: u32, 825 | _offset: u64, 826 | _data: &[u8], 827 | ) -> Result<(), std::io::Error>; 828 | fn dma_map( 829 | &mut self, 830 | _flags: DmaMapFlags, 831 | _offset: u64, 832 | _address: u64, 833 | _size: u64, 834 | _fd: Option, 835 | ) -> Result<(), std::io::Error>; 836 | fn dma_unmap( 837 | &mut self, 838 | _flags: DmaUnmapFlags, 839 | _address: u64, 840 | _size: u64, 841 | ) -> Result<(), std::io::Error>; 842 | fn reset(&mut self) -> Result<(), std::io::Error>; 843 | fn set_irqs( 844 | &mut self, 845 | _index: u32, 846 | _flags: u32, 847 | _start: u32, 848 | _count: u32, 849 | _fds: Vec, 850 | ) -> Result<(), std::io::Error>; 851 | } 852 | 853 | pub struct Server { 854 | listener: UnixListener, 855 | resettable: bool, 856 | irqs: Vec, 857 | regions: Vec, 858 | } 859 | 860 | impl Server { 861 | pub fn new( 862 | path: &Path, 863 | resettable: bool, 864 | irqs: Vec, 865 | regions: Vec, 866 | ) -> Result { 867 | let listener = UnixListener::bind(path).map_err(Error::SocketBind)?; 868 | 869 | Ok(Server { 870 | listener, 871 | resettable, 872 | irqs, 873 | regions, 874 | }) 875 | } 876 | 877 | fn handle_command( 878 | &self, 879 | backend: &mut dyn ServerBackend, 880 | stream: &mut UnixStream, 881 | header: Header, 882 | fds: Vec, 883 | ) -> Result<(), Error> { 884 | match header.command { 885 | Command::Unknown 886 | | Command::GetRegionIoFds 887 | | Command::DmaRead 888 | | Command::DmaWrite 889 | | Command::UserDirtyPages => { 890 | return Err(Error::UnsupportedCommand(header.command)); 891 | } 892 | Command::Version => { 893 | // TODO: Make version/capabilities configurable 894 | let mut client_version = Version { 895 | header, 896 | ..Default::default() 897 | }; 898 | stream 899 | .read_exact(&mut client_version.as_mut_slice()[size_of::
()..]) 900 | .map_err(Error::StreamRead)?; 901 | 902 | let mut raw_version_data = 903 | vec![0; header.message_size as usize - size_of::()]; 904 | stream 905 | .read_exact(&mut raw_version_data) 906 | .map_err(Error::StreamRead)?; 907 | let client_version_data = CString::from_vec_with_nul(raw_version_data) 908 | .unwrap() 909 | .to_string_lossy() 910 | .into_owned(); 911 | let client_capabilities: CapabilitiesData = 912 | serde_json::from_str(&client_version_data) 913 | .map_err(Error::DeserializeCapabilites)?; 914 | 915 | info!( 916 | "Received client version: major = {} minor = {} capabilities = {:?}", 917 | client_version.major, client_version.minor, client_capabilities.capabilities, 918 | ); 919 | 920 | let server_capabilities = CapabilitiesData::default(); 921 | let server_version_data = serde_json::to_string(&server_capabilities) 922 | .map_err(Error::SerializeCapabilites)?; 923 | let server_version = Version { 924 | header: Header { 925 | message_id: client_version.header.message_id, 926 | command: Command::Version, 927 | flags: HeaderFlags::Reply as u32, 928 | message_size: (size_of::() + server_version_data.len() + 1) as u32, 929 | ..Default::default() 930 | }, 931 | major: 0, 932 | minor: 0, 933 | }; 934 | 935 | let server_version_data = CString::new(server_version_data.as_bytes()).unwrap(); 936 | 937 | let bufs = vec![ 938 | IoSlice::new(server_version.as_slice()), 939 | IoSlice::new(server_version_data.as_bytes_with_nul()), 940 | ]; 941 | 942 | // TODO: Use write_all_vectored() when ready 943 | let _ = stream.write_vectored(&bufs).map_err(Error::StreamWrite)?; 944 | 945 | info!( 946 | "Sent server version: major = {} minor = {} capabilities = {:?}", 947 | server_version.major, server_version.minor, server_capabilities.capabilities 948 | ); 949 | } 950 | Command::DmaMap => { 951 | let mut cmd = DmaMap { 952 | header, 953 | ..Default::default() 954 | }; 955 | stream 956 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 957 | .map_err(Error::StreamRead)?; 958 | 959 | let mut fds = fds; 960 | 961 | // The specification demands that the caller passes 0 962 | // or 1 file descriptor. 963 | if fds.len() > 1 { 964 | return Err(Error::InvalidInput); 965 | } 966 | 967 | backend 968 | .dma_map( 969 | DmaMapFlags::from_bits_truncate(cmd.flags), 970 | cmd.offset, 971 | cmd.address, 972 | cmd.size, 973 | fds.pop(), 974 | ) 975 | .map_err(Error::Backend)?; 976 | 977 | let reply = Header { 978 | message_id: cmd.header.message_id, 979 | command: Command::DmaMap, 980 | flags: HeaderFlags::Reply as u32, 981 | message_size: size_of::
() as u32, 982 | ..Default::default() 983 | }; 984 | stream 985 | .write_all(reply.as_slice()) 986 | .map_err(Error::StreamWrite)?; 987 | } 988 | Command::DmaUnmap => { 989 | let mut cmd = DmaUnmap { 990 | header, 991 | ..Default::default() 992 | }; 993 | stream 994 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 995 | .map_err(Error::StreamRead)?; 996 | 997 | backend 998 | .dma_unmap( 999 | DmaUnmapFlags::from_bits_truncate(cmd.flags), 1000 | cmd.address, 1001 | cmd.size, 1002 | ) 1003 | .map_err(Error::Backend)?; 1004 | 1005 | let reply = DmaUnmap { 1006 | header: Header { 1007 | message_id: cmd.header.message_id, 1008 | command: Command::DmaUnmap, 1009 | flags: HeaderFlags::Reply as u32, 1010 | message_size: size_of::() as u32, 1011 | ..Default::default() 1012 | }, 1013 | argsz: cmd.argsz, 1014 | flags: cmd.flags, 1015 | address: cmd.address, 1016 | size: cmd.size, 1017 | }; 1018 | stream 1019 | .write_all(reply.as_slice()) 1020 | .map_err(Error::StreamWrite)?; 1021 | } 1022 | Command::DeviceGetInfo => { 1023 | let mut cmd = DeviceGetInfo { 1024 | header, 1025 | ..Default::default() 1026 | }; 1027 | stream 1028 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 1029 | .map_err(Error::StreamRead)?; 1030 | 1031 | let reply = DeviceGetInfo { 1032 | header: Header { 1033 | message_id: cmd.header.message_id, 1034 | command: Command::DeviceGetInfo, 1035 | flags: HeaderFlags::Reply as u32, 1036 | message_size: size_of::() as u32, 1037 | ..Default::default() 1038 | }, 1039 | argsz: size_of::() as u32 - size_of::
() as u32, 1040 | // TODO: Consider non-PCI devices 1041 | flags: VFIO_DEVICE_FLAGS_PCI 1042 | | if self.resettable { 1043 | VFIO_DEVICE_FLAGS_RESET 1044 | } else { 1045 | 0 1046 | }, 1047 | num_regions: self.regions.len() as u32, 1048 | num_irqs: self.irqs.len() as u32, 1049 | }; 1050 | stream 1051 | .write_all(reply.as_slice()) 1052 | .map_err(Error::StreamWrite)?; 1053 | } 1054 | Command::DeviceGetRegionInfo => { 1055 | let mut cmd = DeviceGetRegionInfo { 1056 | header, 1057 | ..Default::default() 1058 | }; 1059 | stream 1060 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 1061 | .map_err(Error::StreamRead)?; 1062 | 1063 | if cmd.region_info.index as usize >= self.regions.len() { 1064 | return Err(Error::InvalidInput); 1065 | } 1066 | 1067 | // TODO: Need to handle region capabilities e.g. sparse regions 1068 | let reply = DeviceGetRegionInfo { 1069 | header: Header { 1070 | message_id: cmd.header.message_id, 1071 | command: Command::DeviceGetRegionInfo, 1072 | flags: HeaderFlags::Reply as u32, 1073 | message_size: size_of::() as u32, 1074 | ..Default::default() 1075 | }, 1076 | region_info: self.regions[cmd.region_info.index as usize], 1077 | }; 1078 | stream 1079 | .write_all(reply.as_slice()) 1080 | .map_err(Error::StreamWrite)?; 1081 | } 1082 | Command::GetIrqInfo => { 1083 | let mut cmd = GetIrqInfo { 1084 | header, 1085 | ..Default::default() 1086 | }; 1087 | stream 1088 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 1089 | .map_err(Error::StreamRead)?; 1090 | 1091 | if cmd.index as usize >= self.irqs.len() { 1092 | return Err(Error::InvalidInput); 1093 | } 1094 | 1095 | let irq = &self.irqs[cmd.index as usize]; 1096 | 1097 | let reply = GetIrqInfo { 1098 | header: Header { 1099 | message_id: cmd.header.message_id, 1100 | command: Command::GetIrqInfo, 1101 | flags: HeaderFlags::Reply as u32, 1102 | message_size: size_of::() as u32, 1103 | ..Default::default() 1104 | }, 1105 | argsz: (size_of::() - size_of::
()) as u32, 1106 | index: irq.index, 1107 | flags: irq.flags, 1108 | count: irq.count, 1109 | }; 1110 | stream 1111 | .write_all(reply.as_slice()) 1112 | .map_err(Error::StreamWrite)?; 1113 | } 1114 | Command::SetIrqs => { 1115 | let mut cmd = SetIrqs { 1116 | header, 1117 | ..Default::default() 1118 | }; 1119 | stream 1120 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 1121 | .map_err(Error::StreamRead)?; 1122 | 1123 | if cmd.index as usize >= self.irqs.len() { 1124 | return Err(Error::InvalidInput); 1125 | } 1126 | 1127 | if cmd.flags & VFIO_IRQ_SET_DATA_BOOL > 0 { 1128 | return Err(Error::UnsupportedFeature); 1129 | } 1130 | 1131 | backend 1132 | .set_irqs(cmd.index, cmd.flags, cmd.start, cmd.count, fds) 1133 | .map_err(Error::Backend)?; 1134 | 1135 | let reply = Header { 1136 | message_id: cmd.header.message_id, 1137 | command: Command::SetIrqs, 1138 | flags: HeaderFlags::Reply as u32, 1139 | message_size: size_of::
() as u32, 1140 | ..Default::default() 1141 | }; 1142 | stream 1143 | .write_all(reply.as_slice()) 1144 | .map_err(Error::StreamWrite)?; 1145 | } 1146 | Command::RegionRead => { 1147 | let mut cmd = RegionAccess { 1148 | header, 1149 | ..Default::default() 1150 | }; 1151 | stream 1152 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 1153 | .map_err(Error::StreamRead)?; 1154 | 1155 | let (region, offset, count) = (cmd.region, cmd.offset, cmd.count); 1156 | 1157 | if region as usize >= self.regions.len() { 1158 | return Err(Error::InvalidInput); 1159 | } 1160 | 1161 | let mut data = vec![0u8; count as usize]; 1162 | backend 1163 | .region_read(region, offset, &mut data) 1164 | .map_err(Error::Backend)?; 1165 | 1166 | let reply = RegionAccess { 1167 | header: Header { 1168 | message_id: cmd.header.message_id, 1169 | command: Command::RegionRead, 1170 | flags: HeaderFlags::Reply as u32, 1171 | message_size: size_of::() as u32 + count, 1172 | ..Default::default() 1173 | }, 1174 | region, 1175 | offset, 1176 | count, 1177 | }; 1178 | stream 1179 | .write_all(reply.as_slice()) 1180 | .map_err(Error::StreamWrite)?; 1181 | stream.write_all(&data).map_err(Error::StreamWrite)?; 1182 | } 1183 | Command::RegionWrite => { 1184 | let mut cmd = RegionAccess { 1185 | header, 1186 | ..Default::default() 1187 | }; 1188 | stream 1189 | .read_exact(&mut cmd.as_mut_slice()[size_of::
()..]) 1190 | .map_err(Error::StreamRead)?; 1191 | 1192 | let (region, offset, count) = (cmd.region, cmd.offset, cmd.count); 1193 | 1194 | if region as usize >= self.regions.len() { 1195 | return Err(Error::InvalidInput); 1196 | } 1197 | 1198 | let mut data = vec![0u8; count as usize]; 1199 | stream.read_exact(&mut data).map_err(Error::StreamRead)?; 1200 | backend 1201 | .region_write(region, offset, &data) 1202 | .map_err(Error::Backend)?; 1203 | 1204 | let reply = RegionAccess { 1205 | header: Header { 1206 | message_id: cmd.header.message_id, 1207 | command: Command::RegionWrite, 1208 | flags: HeaderFlags::Reply as u32, 1209 | message_size: size_of::() as u32, 1210 | ..Default::default() 1211 | }, 1212 | region, 1213 | offset, 1214 | count, 1215 | }; 1216 | stream 1217 | .write_all(reply.as_slice()) 1218 | .map_err(Error::StreamWrite)?; 1219 | } 1220 | Command::DeviceReset => { 1221 | backend.reset().map_err(Error::Backend)?; 1222 | let reply = Header { 1223 | message_id: header.message_id, 1224 | command: Command::DeviceReset, 1225 | flags: HeaderFlags::Reply as u32, 1226 | message_size: size_of::
() as u32, 1227 | ..Default::default() 1228 | }; 1229 | stream 1230 | .write_all(reply.as_slice()) 1231 | .map_err(Error::StreamWrite)?; 1232 | } 1233 | } 1234 | 1235 | Ok(()) 1236 | } 1237 | 1238 | pub fn run(&self, backend: &mut dyn ServerBackend) -> Result<(), Error> { 1239 | let (mut stream, _) = self.listener.accept().map_err(Error::SocketAccept)?; 1240 | 1241 | loop { 1242 | let mut header = Header::default(); 1243 | 1244 | // The maximum number of FDs that can be sent is 16 so that is 1245 | // also the maximum that can be received. 1246 | let mut fds = vec![0; 16]; 1247 | let mut iovecs = vec![iovec { 1248 | iov_base: header.as_mut_slice().as_mut_ptr() as *mut c_void, 1249 | iov_len: header.as_mut_slice().len(), 1250 | }]; 1251 | // SAFETY: Safe as the iovect is correctly initialised and fds is big enough 1252 | let (bytes, fds_received) = unsafe { 1253 | stream 1254 | .recv_with_fds(&mut iovecs, &mut fds) 1255 | .map_err(Error::ReceiveWithFd)? 1256 | }; 1257 | 1258 | // Other end closed connection 1259 | if bytes == 0 { 1260 | info!("Connection closed"); 1261 | break; 1262 | } 1263 | 1264 | fds.resize(fds_received, 0); 1265 | 1266 | let fds: Vec = fds 1267 | .iter() 1268 | // SAFETY: Safe as we have only valid FDs in the vector now 1269 | .map(|fd| unsafe { File::from_raw_fd(*fd) }) 1270 | .collect(); 1271 | 1272 | if let Err(e) = self.handle_command(backend, &mut stream, header, fds) { 1273 | error!("Error handling command: {:?}: {e}", header.command); 1274 | let reply = Header { 1275 | message_id: header.message_id, 1276 | command: header.command, 1277 | flags: HeaderFlags::Error as u32, 1278 | message_size: size_of::
() as u32, 1279 | error: if matches!(e, Error::InvalidInput) { 1280 | EINVAL as u32 1281 | } else { 1282 | 0 1283 | }, 1284 | }; 1285 | stream 1286 | .write_all(reply.as_slice()) 1287 | .map_err(Error::StreamWrite)?; 1288 | } 1289 | } 1290 | 1291 | Ok(()) 1292 | } 1293 | } 1294 | --------------------------------------------------------------------------------