pci-driver-0.1.3/.cargo_vcs_info.json0000644000000001360000000000100131010ustar { "git": { "sha1": "baa92d6c4e59fad480d90840f51c5db1ab00af2d" }, "path_in_vcs": "" }pci-driver-0.1.3/.editorconfig000064400000000000000000000002721046102023000143470ustar 00000000000000root = true [*] charset = utf-8 end_of_line = lf indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true [*.{md,rst,yaml,yml}] indent_size = 2 pci-driver-0.1.3/.gitignore000064400000000000000000000000241046102023000136550ustar 00000000000000/Cargo.lock /target pci-driver-0.1.3/.gitlab-ci.yml000064400000000000000000000006121046102023000143240ustar 00000000000000rust-stable: parallel: matrix: - IMAGE: - amd64/rust - i386/rust PCI_DRIVER_FEATURES: - "" - vfio image: $IMAGE:latest before_script: - rustup component add clippy rustfmt script: - PCI_DRIVER_FEATURES+=,_unsafe-op-in-unsafe-fn ./test.sh rust-1.47: extends: rust-stable image: $IMAGE:1.47 script: - ./test.sh pci-driver-0.1.3/Cargo.toml0000644000000020560000000000100111020ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "pci-driver" version = "0.1.3" description = "PCI/PCIe driver development library" readme = "README.md" keywords = [ "PCI", "PCIe", "driver", "user-space", ] categories = ["hardware-support"] license = "MIT OR Apache-2.0" repository = "https://gitlab.com/pci-driver/pci-driver" [dependencies.libc] version = "0.2" optional = true default-features = false [dependencies.num-traits] version = "0.2" default-features = false [dev-dependencies.byte-strings] version = "0.2" [features] _unsafe-op-in-unsafe-fn = [] default = ["vfio"] vfio = ["libc/std"] pci-driver-0.1.3/Cargo.toml.orig000064400000000000000000000010521046102023000145560ustar 00000000000000[package] name = "pci-driver" version = "0.1.3" edition = "2018" description = "PCI/PCIe driver development library" license = "MIT OR Apache-2.0" repository = "https://gitlab.com/pci-driver/pci-driver" keywords = ["PCI", "PCIe", "driver", "user-space"] categories = ["hardware-support"] [features] default = ["vfio"] vfio = ["libc/std"] _unsafe-op-in-unsafe-fn = [] [dependencies] libc = { version = "0.2", default-features = false, optional = true } num-traits = { version = "0.2", default-features = false } [dev-dependencies] byte-strings = "0.2" pci-driver-0.1.3/LICENSE-APACHE000064400000000000000000000261351046102023000136240ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pci-driver-0.1.3/LICENSE-MIT000064400000000000000000000020741046102023000133300ustar 00000000000000MIT License Copyright (c) 2022 The pci-driver Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pci-driver-0.1.3/README.md000064400000000000000000000013661046102023000131560ustar 00000000000000# pci-driver pci-driver is a Rust crate that enables you to develop user-space PCI and PCIe drivers. It currently achieves this using Linux's VFIO, but is designed to be extended to other "backends" in the future. pci-driver is available on crates.io at https://crates.io/crates/pci-driver. The documentation is at https://docs.rs/pci-driver. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)) * MIT license ([LICENSE-MIT](LICENSE-MIT)) at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. pci-driver-0.1.3/src/backends/mock.rs000064400000000000000000000655221046102023000155430ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use byte_strings::concat_bytes; use std::io::{self, ErrorKind}; use std::ops::Range; use std::os::unix::io::RawFd; use crate::config::PciConfig; use crate::device::{PciDevice, PciDeviceInternal, Sealed}; use crate::interrupts::{PciInterruptKind, PciInterrupts}; use crate::iommu::{PciIommu, PciIommuInternal}; use crate::regions::BackedByPciSubregion; use crate::regions::{OwningPciRegion, PciRegion, Permissions, RegionIdentifier}; /* ---------------------------------------------------------------------------------------------- */ #[derive(Debug)] pub(crate) struct MockPciDevice; impl Sealed for MockPciDevice {} impl PciDevice for MockPciDevice { fn config(&self) -> PciConfig { PciConfig::backed_by(&MockConfigSpace as &dyn PciRegion) } fn bar(&self, _index: usize) -> Option { todo!() } fn rom(&self) -> Option { todo!() } fn iommu(&self) -> PciIommu { PciIommu { internal: self } } fn interrupts(&self) -> PciInterrupts { PciInterrupts { device: self } } fn reset(&self) -> io::Result<()> { todo!() } } impl PciDeviceInternal for MockPciDevice { fn region_map( &self, _identifier: RegionIdentifier, _offset: u64, _len: usize, _permissions: Permissions, ) -> io::Result<*mut u8> { todo!() } unsafe fn region_unmap( &self, _identifier: RegionIdentifier, _address: *mut u8, _length: usize, ) { todo!() } fn interrupts_max(&self, _kind: PciInterruptKind) -> usize { todo!() } fn interrupts_enable(&self, _kind: PciInterruptKind, _eventfds: &[RawFd]) -> io::Result<()> { todo!() } fn interrupts_disable(&self, _kind: PciInterruptKind) -> io::Result<()> { todo!() } } impl PciIommuInternal for MockPciDevice { fn alignment(&self) -> usize { todo!() } fn valid_iova_ranges(&self) -> &[Range] { todo!() } fn max_num_mappings(&self) -> u32 { todo!() } unsafe fn map( &self, _iova: u64, _size: usize, _address: *const u8, _device_permissions: Permissions, ) -> io::Result<()> { todo!() } fn unmap(&self, _iova: u64, _size: usize) -> io::Result<()> { todo!() } } /* ---------------------------------------------------------------------------------------------- */ #[derive(Debug)] struct MockConfigSpace; impl crate::regions::Sealed for MockConfigSpace {} impl PciRegion for MockConfigSpace { fn len(&self) -> u64 { CONFIG_SPACE.len() as u64 } fn permissions(&self) -> Permissions { Permissions::Read } fn as_ptr(&self) -> Option<*const u8> { todo!() } fn as_mut_ptr(&self) -> Option<*mut u8> { todo!() } fn read_bytes(&self, _offset: u64, _buffer: &mut [u8]) -> io::Result<()> { todo!() } fn read_u8(&self, offset: u64) -> io::Result { Ok(CONFIG_SPACE[offset as usize]) } fn write_u8(&self, _offset: u64, _value: u8) -> io::Result<()> { Err(io::Error::new( ErrorKind::PermissionDenied, "Config space writes not supported", )) } fn read_le_u16(&self, offset: u64) -> io::Result { let mut buffer = [0; 2]; buffer.copy_from_slice(&CONFIG_SPACE[offset as usize..offset as usize + 2]); Ok(u16::from_le_bytes(buffer)) } fn write_le_u16(&self, _offset: u64, _value: u16) -> io::Result<()> { Err(io::Error::new( ErrorKind::PermissionDenied, "Config space writes not supported", )) } fn read_le_u32(&self, offset: u64) -> io::Result { let mut buffer = [0; 4]; buffer.copy_from_slice(&CONFIG_SPACE[offset as usize..offset as usize + 4]); Ok(u32::from_le_bytes(buffer)) } fn write_le_u32(&self, _offset: u64, _value: u32) -> io::Result<()> { Err(io::Error::new( ErrorKind::PermissionDenied, "Config space writes not supported", )) } } /* ---------------------------------------------------------------------------------------------- */ // 55:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd NVMe SSD Controller SM981/PM981/PM983 (prog-if 02 [NVM Express]) // Subsystem: Samsung Electronics Co Ltd SSD 970 EVO Plus 1TB // Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+ // Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- SERR- // Capabilities: [168 v1] Secondary PCI Express // LnkCtl3: LnkEquIntrruptEn- PerformEqu- // LaneErrStat: 0 // Capabilities: [188 v1] Latency Tolerance Reporting // Max snoop latency: 3145728ns // Max no snoop latency: 3145728ns // Capabilities: [190 v1] L1 PM Substates // L1SubCap: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ L1_PM_Substates+ // PortCommonModeRestoreTime=10us PortTPowerOnTime=10us // L1SubCtl1: PCI-PM_L1.2+ PCI-PM_L1.1+ ASPM_L1.2+ ASPM_L1.1+ // T_CommonMode=0us LTR1.2_Threshold=65536ns // L1SubCtl2: T_PwrOn=44us // Kernel driver in use: nvme // Kernel modules: nvme const CONFIG_SPACE: &[u8; 4096] = concat_bytes!( b"\x4d\x14\x08\xa8\x06\x04\x10\x00\x00\x02\x08\x01\x00\x00\x00\x00", b"\x04\x00\x20\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4d\x14\x01\xa8", b"\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00", b"\x01\x50\x03\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x05\x70\x8a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x10\xb0\x02\x00\xc1\x8f\xe8\x17\x30\x28\x00\x00\x43\x78\x47\x00", b"\x42\x01\x43\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x1f\x08\x00\x00\x00\x04\x00\x00\x0e\x00\x00\x00", b"\x03\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x11\x00\x20\x80\x00\x30\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // ------------- below is PCI extended config space ------------- // b"\x01\x00\x82\x14\x00\x00\x00\x00\x00\x00\x40\x00\x30\x20\x46\x00", b"\x00\x00\x00\x00\x00\xe0\x00\x00\xa0\x02\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x81\x15\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x81\x16\x00\x00\x00\x00", b"\x00\x00\x00\x00\x01\x00\x00\x00\x19\x00\x81\x18\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x75\x00\x75\x00\x75\x00\x75\x00\x7f\x00\x7f", b"\x00\x7f\x00\x7f\x00\x00\x00\x00\x18\x00\x01\x19\x03\x10\x03\x10", b"\x1e\x00\x01\x00\x1f\x0a\x28\x00\x0f\x00\x02\x60\xb0\x00\x00\x00", b"\x16\x00\x01\x1d\x1f\x11\x80\xff\x00\x00\x00\x00\x00\x01\x00\x00", b"\x20\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18\x17\x16\x15\x14\x13\x12\x11", b"\x10\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01", b"\x22\x00\xc1\x1d\x00\x00\x00\x80\x00\x00\x00\x00\x0b\x00\xc1\x2d", b"\x02\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00", b"\xa0\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x04", b"\x00\x00\x48\x00\x01\x00\x2c\x00\xb6\x57\x7b\x03\x00\xc9\x40\x01", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00", b"\x43\x46\x1c\x00\xc3\x65\x28\x20\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x41\x31", b"\x01\x00\x81\x03\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00", b"\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x00\xc0\x00", b"\xc0\x00\xc0\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x1f\x00\x01\x32\x01\x00\x00\x00\x00\x00\x00\x00", b"\x0b\x00\x01\x00\x03\x00\x41\x05\x00\x09\x00\x00\x02\x00\x00\x00", b"\x64\x5c\xc4\x93\xa2\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00", b"\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x0e\x00\x2c\x00\xff\xff\xff\xff\x04\x00\x00\x07\x00\xff\xff\x5b", b"\x20\x01\x07\x00\x00\x00\x00\x00\x00\x40\x01\x00\x00\x05\x00\x00", b"\x00\x00\x00\x00\x01\x00\x00\x00\x91\xcb\x09\x03\x10\x00\x00\x08", b"\x40\x01\x03\x00\x01\x00\x03\x00\xff\xff\x0f\x00\x00\x00\x00\x00", b"\x0f\x00\x00\x00\x00\x00\x00\x00\x00\xb1\x24\x40\x7c\xb0\x24\x00", b"\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x01\x01\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x21\xff\x03\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x01\x00\x00\x00", b"\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x32", b"\x01\x80\x00\x00\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\xf4\x09\xf4\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x0d\x00\x00\x00\xd2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x20\x00\x00\x00\x00\x00\x00\x00\x46\x88\x46\x88\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", ); /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/backends/mod.rs000064400000000000000000000005051046102023000153570ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ #[cfg(feature = "vfio")] pub mod vfio; #[cfg(test)] pub(crate) mod mock; /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/backends/vfio/bindings.rs000064400000000000000000001042341046102023000173440ustar 00000000000000// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note // This file was generated by running // // bindgen include/uapi/linux/vfio.h --allowlist-file include/uapi/linux/vfio.h \ // --with-derive-default --no-layout-tests -o bindings.rs -- -I include // // at the root of the Linux 5.19.0 tree. The SPDX header and this note were then added manually. /* automatically generated by rust-bindgen 0.60.1 */ #[repr(C)] #[derive(Default)] pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); impl __IncompleteArrayField { #[inline] pub const fn new() -> Self { __IncompleteArrayField(::std::marker::PhantomData, []) } #[inline] pub fn as_ptr(&self) -> *const T { self as *const _ as *const T } #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { self as *mut _ as *mut T } #[inline] pub unsafe fn as_slice(&self, len: usize) -> &[T] { ::std::slice::from_raw_parts(self.as_ptr(), len) } #[inline] pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) } } impl ::std::fmt::Debug for __IncompleteArrayField { fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fmt.write_str("__IncompleteArrayField") } } pub const VFIO_API_VERSION: u32 = 0; pub const VFIO_TYPE1_IOMMU: u32 = 1; pub const VFIO_SPAPR_TCE_IOMMU: u32 = 2; pub const VFIO_TYPE1v2_IOMMU: u32 = 3; pub const VFIO_DMA_CC_IOMMU: u32 = 4; pub const VFIO_EEH: u32 = 5; pub const VFIO_TYPE1_NESTING_IOMMU: u32 = 6; pub const VFIO_SPAPR_TCE_v2_IOMMU: u32 = 7; pub const VFIO_NOIOMMU_IOMMU: u32 = 8; pub const VFIO_UNMAP_ALL: u32 = 9; pub const VFIO_UPDATE_VADDR: u32 = 10; pub const VFIO_TYPE: u8 = 59u8; pub const VFIO_BASE: u32 = 100; pub const VFIO_GROUP_FLAGS_VIABLE: u32 = 1; pub const VFIO_GROUP_FLAGS_CONTAINER_SET: u32 = 2; pub const VFIO_DEVICE_FLAGS_RESET: u32 = 1; pub const VFIO_DEVICE_FLAGS_PCI: u32 = 2; pub const VFIO_DEVICE_FLAGS_PLATFORM: u32 = 4; pub const VFIO_DEVICE_FLAGS_AMBA: u32 = 8; pub const VFIO_DEVICE_FLAGS_CCW: u32 = 16; pub const VFIO_DEVICE_FLAGS_AP: u32 = 32; pub const VFIO_DEVICE_FLAGS_FSL_MC: u32 = 64; pub const VFIO_DEVICE_FLAGS_CAPS: u32 = 128; pub const VFIO_DEVICE_API_PCI_STRING: &[u8; 9usize] = b"vfio-pci\0"; pub const VFIO_DEVICE_API_PLATFORM_STRING: &[u8; 14usize] = b"vfio-platform\0"; pub const VFIO_DEVICE_API_AMBA_STRING: &[u8; 10usize] = b"vfio-amba\0"; pub const VFIO_DEVICE_API_CCW_STRING: &[u8; 9usize] = b"vfio-ccw\0"; pub const VFIO_DEVICE_API_AP_STRING: &[u8; 8usize] = b"vfio-ap\0"; pub const VFIO_DEVICE_INFO_CAP_ZPCI_BASE: u32 = 1; pub const VFIO_DEVICE_INFO_CAP_ZPCI_GROUP: u32 = 2; pub const VFIO_DEVICE_INFO_CAP_ZPCI_UTIL: u32 = 3; pub const VFIO_DEVICE_INFO_CAP_ZPCI_PFIP: u32 = 4; pub const VFIO_REGION_INFO_FLAG_READ: u32 = 1; pub const VFIO_REGION_INFO_FLAG_WRITE: u32 = 2; pub const VFIO_REGION_INFO_FLAG_MMAP: u32 = 4; pub const VFIO_REGION_INFO_FLAG_CAPS: u32 = 8; pub const VFIO_REGION_INFO_CAP_SPARSE_MMAP: u32 = 1; pub const VFIO_REGION_INFO_CAP_TYPE: u32 = 2; pub const VFIO_REGION_TYPE_PCI_VENDOR_TYPE: u32 = 2147483648; pub const VFIO_REGION_TYPE_PCI_VENDOR_MASK: u32 = 65535; pub const VFIO_REGION_TYPE_GFX: u32 = 1; pub const VFIO_REGION_TYPE_CCW: u32 = 2; pub const VFIO_REGION_TYPE_MIGRATION_DEPRECATED: u32 = 3; pub const VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION: u32 = 1; pub const VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG: u32 = 2; pub const VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG: u32 = 3; pub const VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM: u32 = 1; pub const VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD: u32 = 1; pub const VFIO_REGION_SUBTYPE_GFX_EDID: u32 = 1; pub const VFIO_DEVICE_GFX_LINK_STATE_UP: u32 = 1; pub const VFIO_DEVICE_GFX_LINK_STATE_DOWN: u32 = 2; pub const VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD: u32 = 1; pub const VFIO_REGION_SUBTYPE_CCW_SCHIB: u32 = 2; pub const VFIO_REGION_SUBTYPE_CCW_CRW: u32 = 3; pub const VFIO_REGION_SUBTYPE_MIGRATION_DEPRECATED: u32 = 1; pub const VFIO_DEVICE_STATE_V1_STOP: u32 = 0; pub const VFIO_DEVICE_STATE_V1_RUNNING: u32 = 1; pub const VFIO_DEVICE_STATE_V1_SAVING: u32 = 2; pub const VFIO_DEVICE_STATE_V1_RESUMING: u32 = 4; pub const VFIO_DEVICE_STATE_MASK: u32 = 7; pub const VFIO_REGION_INFO_CAP_MSIX_MAPPABLE: u32 = 3; pub const VFIO_REGION_INFO_CAP_NVLINK2_SSATGT: u32 = 4; pub const VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD: u32 = 5; pub const VFIO_IRQ_INFO_EVENTFD: u32 = 1; pub const VFIO_IRQ_INFO_MASKABLE: u32 = 2; pub const VFIO_IRQ_INFO_AUTOMASKED: u32 = 4; pub const VFIO_IRQ_INFO_NORESIZE: u32 = 8; pub const VFIO_IRQ_SET_DATA_NONE: u32 = 1; pub const VFIO_IRQ_SET_DATA_BOOL: u32 = 2; pub const VFIO_IRQ_SET_DATA_EVENTFD: u32 = 4; pub const VFIO_IRQ_SET_ACTION_MASK: u32 = 8; pub const VFIO_IRQ_SET_ACTION_UNMASK: u32 = 16; pub const VFIO_IRQ_SET_ACTION_TRIGGER: u32 = 32; pub const VFIO_IRQ_SET_DATA_TYPE_MASK: u32 = 7; pub const VFIO_IRQ_SET_ACTION_TYPE_MASK: u32 = 56; pub const VFIO_GFX_PLANE_TYPE_PROBE: u32 = 1; pub const VFIO_GFX_PLANE_TYPE_DMABUF: u32 = 2; pub const VFIO_GFX_PLANE_TYPE_REGION: u32 = 4; pub const VFIO_DEVICE_IOEVENTFD_8: u32 = 1; pub const VFIO_DEVICE_IOEVENTFD_16: u32 = 2; pub const VFIO_DEVICE_IOEVENTFD_32: u32 = 4; pub const VFIO_DEVICE_IOEVENTFD_64: u32 = 8; pub const VFIO_DEVICE_IOEVENTFD_SIZE_MASK: u32 = 15; pub const VFIO_DEVICE_FEATURE_MASK: u32 = 65535; pub const VFIO_DEVICE_FEATURE_GET: u32 = 65536; pub const VFIO_DEVICE_FEATURE_SET: u32 = 131072; pub const VFIO_DEVICE_FEATURE_PROBE: u32 = 262144; pub const VFIO_DEVICE_FEATURE_PCI_VF_TOKEN: u32 = 0; pub const VFIO_MIGRATION_STOP_COPY: u32 = 1; pub const VFIO_MIGRATION_P2P: u32 = 2; pub const VFIO_DEVICE_FEATURE_MIGRATION: u32 = 1; pub const VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE: u32 = 2; pub const VFIO_IOMMU_INFO_PGSIZES: u32 = 1; pub const VFIO_IOMMU_INFO_CAPS: u32 = 2; pub const VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE: u32 = 1; pub const VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION: u32 = 2; pub const VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL: u32 = 3; pub const VFIO_DMA_MAP_FLAG_READ: u32 = 1; pub const VFIO_DMA_MAP_FLAG_WRITE: u32 = 2; pub const VFIO_DMA_MAP_FLAG_VADDR: u32 = 4; pub const VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP: u32 = 1; pub const VFIO_DMA_UNMAP_FLAG_ALL: u32 = 2; pub const VFIO_DMA_UNMAP_FLAG_VADDR: u32 = 4; pub const VFIO_IOMMU_DIRTY_PAGES_FLAG_START: u32 = 1; pub const VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP: u32 = 2; pub const VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP: u32 = 4; pub const VFIO_IOMMU_SPAPR_INFO_DDW: u32 = 1; pub const VFIO_EEH_PE_DISABLE: u32 = 0; pub const VFIO_EEH_PE_ENABLE: u32 = 1; pub const VFIO_EEH_PE_UNFREEZE_IO: u32 = 2; pub const VFIO_EEH_PE_UNFREEZE_DMA: u32 = 3; pub const VFIO_EEH_PE_GET_STATE: u32 = 4; pub const VFIO_EEH_PE_STATE_NORMAL: u32 = 0; pub const VFIO_EEH_PE_STATE_RESET: u32 = 1; pub const VFIO_EEH_PE_STATE_STOPPED: u32 = 2; pub const VFIO_EEH_PE_STATE_STOPPED_DMA: u32 = 4; pub const VFIO_EEH_PE_STATE_UNAVAIL: u32 = 5; pub const VFIO_EEH_PE_RESET_DEACTIVATE: u32 = 5; pub const VFIO_EEH_PE_RESET_HOT: u32 = 6; pub const VFIO_EEH_PE_RESET_FUNDAMENTAL: u32 = 7; pub const VFIO_EEH_PE_CONFIGURE: u32 = 8; pub const VFIO_EEH_PE_INJECT_ERR: u32 = 9; pub type __u8 = ::std::os::raw::c_uchar; pub type __u16 = ::std::os::raw::c_ushort; pub type __s32 = ::std::os::raw::c_int; pub type __u32 = ::std::os::raw::c_uint; pub type __u64 = ::std::os::raw::c_ulonglong; #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_info_cap_header { pub id: __u16, pub version: __u16, pub next: __u32, } #[doc = " VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3,"] #[doc = "\t\t\t\t\t\tstruct vfio_group_status)"] #[doc = ""] #[doc = " Retrieve information about the group. Fills in provided"] #[doc = " struct vfio_group_info. Caller sets argsz."] #[doc = " Return: 0 on succes, -errno on failure."] #[doc = " Availability: Always"] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_group_status { pub argsz: __u32, pub flags: __u32, } #[doc = " VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7,"] #[doc = "\t\t\t\t\t\tstruct vfio_device_info)"] #[doc = ""] #[doc = " Retrieve information about the device. Fills in provided"] #[doc = " struct vfio_device_info. Caller sets argsz."] #[doc = " Return: 0 on success, -errno on failure."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_device_info { pub argsz: __u32, pub flags: __u32, pub num_regions: __u32, pub num_irqs: __u32, pub cap_offset: __u32, } #[doc = " VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8,"] #[doc = "\t\t\t\t struct vfio_region_info)"] #[doc = ""] #[doc = " Retrieve information about a device region. Caller provides"] #[doc = " struct vfio_region_info with index value set. Caller sets argsz."] #[doc = " Implementation of region mapping is bus driver specific. This is"] #[doc = " intended to describe MMIO, I/O port, as well as bus specific"] #[doc = " regions (ex. PCI config space). Zero sized regions may be used"] #[doc = " to describe unimplemented regions (ex. unimplemented PCI BARs)."] #[doc = " Return: 0 on success, -errno on failure."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_region_info { pub argsz: __u32, pub flags: __u32, pub index: __u32, pub cap_offset: __u32, pub size: __u64, pub offset: __u64, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_region_sparse_mmap_area { pub offset: __u64, pub size: __u64, } #[repr(C)] #[derive(Debug, Default)] pub struct vfio_region_info_cap_sparse_mmap { pub header: vfio_info_cap_header, pub nr_areas: __u32, pub reserved: __u32, pub areas: __IncompleteArrayField, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_region_info_cap_type { pub header: vfio_info_cap_header, pub type_: __u32, pub subtype: __u32, } #[doc = " struct vfio_region_gfx_edid - EDID region layout."] #[doc = ""] #[doc = " Set display link state and EDID blob."] #[doc = ""] #[doc = " The EDID blob has monitor information such as brand, name, serial"] #[doc = " number, physical size, supported video modes and more."] #[doc = ""] #[doc = " This special region allows userspace (typically qemu) set a virtual"] #[doc = " EDID for the virtual monitor, which allows a flexible display"] #[doc = " configuration."] #[doc = ""] #[doc = " For the edid blob spec look here:"] #[doc = " https://en.wikipedia.org/wiki/Extended_Display_Identification_Data"] #[doc = ""] #[doc = " On linux systems you can find the EDID blob in sysfs:"] #[doc = " /sys/class/drm/${card}/${connector}/edid"] #[doc = ""] #[doc = " You can use the edid-decode ulility (comes with xorg-x11-utils) to"] #[doc = " decode the EDID blob."] #[doc = ""] #[doc = " @edid_offset: location of the edid blob, relative to the"] #[doc = " start of the region (readonly)."] #[doc = " @edid_max_size: max size of the edid blob (readonly)."] #[doc = " @edid_size: actual edid size (read/write)."] #[doc = " @link_state: display link state (read/write)."] #[doc = " VFIO_DEVICE_GFX_LINK_STATE_UP: Monitor is turned on."] #[doc = " VFIO_DEVICE_GFX_LINK_STATE_DOWN: Monitor is turned off."] #[doc = " @max_xres: max display width (0 == no limitation, readonly)."] #[doc = " @max_yres: max display height (0 == no limitation, readonly)."] #[doc = ""] #[doc = " EDID update protocol:"] #[doc = " (1) set link-state to down."] #[doc = " (2) update edid blob and size."] #[doc = " (3) set link-state to up."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_region_gfx_edid { pub edid_offset: __u32, pub edid_max_size: __u32, pub edid_size: __u32, pub max_xres: __u32, pub max_yres: __u32, pub link_state: __u32, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_device_migration_info { pub device_state: __u32, pub reserved: __u32, pub pending_bytes: __u64, pub data_offset: __u64, pub data_size: __u64, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_region_info_cap_nvlink2_ssatgt { pub header: vfio_info_cap_header, pub tgt: __u64, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_region_info_cap_nvlink2_lnkspd { pub header: vfio_info_cap_header, pub link_speed: __u32, pub __pad: __u32, } #[doc = " VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,"] #[doc = "\t\t\t\t struct vfio_irq_info)"] #[doc = ""] #[doc = " Retrieve information about a device IRQ. Caller provides"] #[doc = " struct vfio_irq_info with index value set. Caller sets argsz."] #[doc = " Implementation of IRQ mapping is bus driver specific. Indexes"] #[doc = " using multiple IRQs are primarily intended to support MSI-like"] #[doc = " interrupt blocks. Zero count irq blocks may be used to describe"] #[doc = " unimplemented interrupt types."] #[doc = ""] #[doc = " The EVENTFD flag indicates the interrupt index supports eventfd based"] #[doc = " signaling."] #[doc = ""] #[doc = " The MASKABLE flags indicates the index supports MASK and UNMASK"] #[doc = " actions described below."] #[doc = ""] #[doc = " AUTOMASKED indicates that after signaling, the interrupt line is"] #[doc = " automatically masked by VFIO and the user needs to unmask the line"] #[doc = " to receive new interrupts. This is primarily intended to distinguish"] #[doc = " level triggered interrupts."] #[doc = ""] #[doc = " The NORESIZE flag indicates that the interrupt lines within the index"] #[doc = " are setup as a set and new subindexes cannot be enabled without first"] #[doc = " disabling the entire index. This is used for interrupts like PCI MSI"] #[doc = " and MSI-X where the driver may only use a subset of the available"] #[doc = " indexes, but VFIO needs to enable a specific number of vectors"] #[doc = " upfront. In the case of MSI-X, where the user can enable MSI-X and"] #[doc = " then add and unmask vectors, it's up to userspace to make the decision"] #[doc = " whether to allocate the maximum supported number of vectors or tear"] #[doc = " down setup and incrementally increase the vectors as each is enabled."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_irq_info { pub argsz: __u32, pub flags: __u32, pub index: __u32, pub count: __u32, } #[doc = " VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set)"] #[doc = ""] #[doc = " Set signaling, masking, and unmasking of interrupts. Caller provides"] #[doc = " struct vfio_irq_set with all fields set. 'start' and 'count' indicate"] #[doc = " the range of subindexes being specified."] #[doc = ""] #[doc = " The DATA flags specify the type of data provided. If DATA_NONE, the"] #[doc = " operation performs the specified action immediately on the specified"] #[doc = " interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]:"] #[doc = " flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1."] #[doc = ""] #[doc = " DATA_BOOL allows sparse support for the same on arrays of interrupts."] #[doc = " For example, to mask interrupts [0,1] and [0,3] (but not [0,2]):"] #[doc = " flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3,"] #[doc = " data = {1,0,1}"] #[doc = ""] #[doc = " DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd."] #[doc = " A value of -1 can be used to either de-assign interrupts if already"] #[doc = " assigned or skip un-assigned interrupts. For example, to set an eventfd"] #[doc = " to be trigger for interrupts [0,0] and [0,2]:"] #[doc = " flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3,"] #[doc = " data = {fd1, -1, fd2}"] #[doc = " If index [0,1] is previously set, two count = 1 ioctls calls would be"] #[doc = " required to set [0,0] and [0,2] without changing [0,1]."] #[doc = ""] #[doc = " Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used"] #[doc = " with ACTION_TRIGGER to perform kernel level interrupt loopback testing"] #[doc = " from userspace (ie. simulate hardware triggering)."] #[doc = ""] #[doc = " Setting of an event triggering mechanism to userspace for ACTION_TRIGGER"] #[doc = " enables the interrupt index for the device. Individual subindex interrupts"] #[doc = " can be disabled using the -1 value for DATA_EVENTFD or the index can be"] #[doc = " disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0."] #[doc = ""] #[doc = " Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while"] #[doc = " ACTION_TRIGGER specifies kernel->user signaling."] #[repr(C)] #[derive(Debug, Default)] pub struct vfio_irq_set { pub argsz: __u32, pub flags: __u32, pub index: __u32, pub start: __u32, pub count: __u32, pub data: __IncompleteArrayField<__u8>, } pub const VFIO_PCI_BAR0_REGION_INDEX: _bindgen_ty_2 = 0; pub const VFIO_PCI_BAR1_REGION_INDEX: _bindgen_ty_2 = 1; pub const VFIO_PCI_BAR2_REGION_INDEX: _bindgen_ty_2 = 2; pub const VFIO_PCI_BAR3_REGION_INDEX: _bindgen_ty_2 = 3; pub const VFIO_PCI_BAR4_REGION_INDEX: _bindgen_ty_2 = 4; pub const VFIO_PCI_BAR5_REGION_INDEX: _bindgen_ty_2 = 5; pub const VFIO_PCI_ROM_REGION_INDEX: _bindgen_ty_2 = 6; pub const VFIO_PCI_CONFIG_REGION_INDEX: _bindgen_ty_2 = 7; pub const VFIO_PCI_VGA_REGION_INDEX: _bindgen_ty_2 = 8; pub const VFIO_PCI_NUM_REGIONS: _bindgen_ty_2 = 9; pub type _bindgen_ty_2 = ::std::os::raw::c_uint; pub const VFIO_PCI_INTX_IRQ_INDEX: _bindgen_ty_3 = 0; pub const VFIO_PCI_MSI_IRQ_INDEX: _bindgen_ty_3 = 1; pub const VFIO_PCI_MSIX_IRQ_INDEX: _bindgen_ty_3 = 2; pub const VFIO_PCI_ERR_IRQ_INDEX: _bindgen_ty_3 = 3; pub const VFIO_PCI_REQ_IRQ_INDEX: _bindgen_ty_3 = 4; pub const VFIO_PCI_NUM_IRQS: _bindgen_ty_3 = 5; pub type _bindgen_ty_3 = ::std::os::raw::c_uint; pub const VFIO_CCW_CONFIG_REGION_INDEX: _bindgen_ty_4 = 0; pub const VFIO_CCW_NUM_REGIONS: _bindgen_ty_4 = 1; pub type _bindgen_ty_4 = ::std::os::raw::c_uint; pub const VFIO_CCW_IO_IRQ_INDEX: _bindgen_ty_5 = 0; pub const VFIO_CCW_CRW_IRQ_INDEX: _bindgen_ty_5 = 1; pub const VFIO_CCW_REQ_IRQ_INDEX: _bindgen_ty_5 = 2; pub const VFIO_CCW_NUM_IRQS: _bindgen_ty_5 = 3; pub type _bindgen_ty_5 = ::std::os::raw::c_uint; #[doc = " VFIO_DEVICE_GET_PCI_HOT_RESET_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 12,"] #[doc = "\t\t\t\t\t struct vfio_pci_hot_reset_info)"] #[doc = ""] #[doc = " Return: 0 on success, -errno on failure:"] #[doc = "\t-enospc = insufficient buffer, -enodev = unsupported for device."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_pci_dependent_device { pub group_id: __u32, pub segment: __u16, pub bus: __u8, pub devfn: __u8, } #[repr(C)] #[derive(Debug, Default)] pub struct vfio_pci_hot_reset_info { pub argsz: __u32, pub flags: __u32, pub count: __u32, pub devices: __IncompleteArrayField, } #[doc = " VFIO_DEVICE_PCI_HOT_RESET - _IOW(VFIO_TYPE, VFIO_BASE + 13,"] #[doc = "\t\t\t\t struct vfio_pci_hot_reset)"] #[doc = ""] #[doc = " Return: 0 on success, -errno on failure."] #[repr(C)] #[derive(Debug, Default)] pub struct vfio_pci_hot_reset { pub argsz: __u32, pub flags: __u32, pub count: __u32, pub group_fds: __IncompleteArrayField<__s32>, } #[doc = " VFIO_DEVICE_QUERY_GFX_PLANE - _IOW(VFIO_TYPE, VFIO_BASE + 14,"] #[doc = " struct vfio_device_query_gfx_plane)"] #[doc = ""] #[doc = " Set the drm_plane_type and flags, then retrieve the gfx plane info."] #[doc = ""] #[doc = " flags supported:"] #[doc = " - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_DMABUF are set"] #[doc = " to ask if the mdev supports dma-buf. 0 on support, -EINVAL on no"] #[doc = " support for dma-buf."] #[doc = " - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_REGION are set"] #[doc = " to ask if the mdev supports region. 0 on support, -EINVAL on no"] #[doc = " support for region."] #[doc = " - VFIO_GFX_PLANE_TYPE_DMABUF or VFIO_GFX_PLANE_TYPE_REGION is set"] #[doc = " with each call to query the plane info."] #[doc = " - Others are invalid and return -EINVAL."] #[doc = ""] #[doc = " Note:"] #[doc = " 1. Plane could be disabled by guest. In that case, success will be"] #[doc = " returned with zero-initialized drm_format, size, width and height"] #[doc = " fields."] #[doc = " 2. x_hot/y_hot is set to 0xFFFFFFFF if no hotspot information available"] #[doc = ""] #[doc = " Return: 0 on success, -errno on other failure."] #[repr(C)] #[derive(Copy, Clone)] pub struct vfio_device_gfx_plane_info { pub argsz: __u32, pub flags: __u32, pub drm_plane_type: __u32, pub drm_format: __u32, pub drm_format_mod: __u64, pub width: __u32, pub height: __u32, pub stride: __u32, pub size: __u32, pub x_pos: __u32, pub y_pos: __u32, pub x_hot: __u32, pub y_hot: __u32, pub __bindgen_anon_1: vfio_device_gfx_plane_info__bindgen_ty_1, } #[repr(C)] #[derive(Copy, Clone)] pub union vfio_device_gfx_plane_info__bindgen_ty_1 { pub region_index: __u32, pub dmabuf_id: __u32, } impl Default for vfio_device_gfx_plane_info__bindgen_ty_1 { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() } } } impl Default for vfio_device_gfx_plane_info { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() } } } #[doc = " VFIO_DEVICE_IOEVENTFD - _IOW(VFIO_TYPE, VFIO_BASE + 16,"] #[doc = " struct vfio_device_ioeventfd)"] #[doc = ""] #[doc = " Perform a write to the device at the specified device fd offset, with"] #[doc = " the specified data and width when the provided eventfd is triggered."] #[doc = " vfio bus drivers may not support this for all regions, for all widths,"] #[doc = " or at all. vfio-pci currently only enables support for BAR regions,"] #[doc = " excluding the MSI-X vector table."] #[doc = ""] #[doc = " Return: 0 on success, -errno on failure."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_device_ioeventfd { pub argsz: __u32, pub flags: __u32, pub offset: __u64, pub data: __u64, pub fd: __s32, } #[doc = " VFIO_DEVICE_FEATURE - _IOWR(VFIO_TYPE, VFIO_BASE + 17,"] #[doc = "\t\t\t struct vfio_device_feature)"] #[doc = ""] #[doc = " Get, set, or probe feature data of the device. The feature is selected"] #[doc = " using the FEATURE_MASK portion of the flags field. Support for a feature"] #[doc = " can be probed by setting both the FEATURE_MASK and PROBE bits. A probe"] #[doc = " may optionally include the GET and/or SET bits to determine read vs write"] #[doc = " access of the feature respectively. Probing a feature will return success"] #[doc = " if the feature is supported and all of the optionally indicated GET/SET"] #[doc = " methods are supported. The format of the data portion of the structure is"] #[doc = " specific to the given feature. The data portion is not required for"] #[doc = " probing. GET and SET are mutually exclusive, except for use with PROBE."] #[doc = ""] #[doc = " Return 0 on success, -errno on failure."] #[repr(C)] #[derive(Debug, Default)] pub struct vfio_device_feature { pub argsz: __u32, pub flags: __u32, pub data: __IncompleteArrayField<__u8>, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_device_feature_migration { pub flags: __u64, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_device_feature_mig_state { pub device_state: __u32, pub data_fd: __s32, } pub const vfio_device_mig_state_VFIO_DEVICE_STATE_ERROR: vfio_device_mig_state = 0; pub const vfio_device_mig_state_VFIO_DEVICE_STATE_STOP: vfio_device_mig_state = 1; pub const vfio_device_mig_state_VFIO_DEVICE_STATE_RUNNING: vfio_device_mig_state = 2; pub const vfio_device_mig_state_VFIO_DEVICE_STATE_STOP_COPY: vfio_device_mig_state = 3; pub const vfio_device_mig_state_VFIO_DEVICE_STATE_RESUMING: vfio_device_mig_state = 4; pub const vfio_device_mig_state_VFIO_DEVICE_STATE_RUNNING_P2P: vfio_device_mig_state = 5; pub type vfio_device_mig_state = ::std::os::raw::c_uint; #[doc = " VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info)"] #[doc = ""] #[doc = " Retrieve information about the IOMMU object. Fills in provided"] #[doc = " struct vfio_iommu_info. Caller sets argsz."] #[doc = ""] #[doc = " XXX Should we do these by CHECK_EXTENSION too?"] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_type1_info { pub argsz: __u32, pub flags: __u32, pub iova_pgsizes: __u64, pub cap_offset: __u32, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iova_range { pub start: __u64, pub end: __u64, } #[repr(C)] #[derive(Debug, Default)] pub struct vfio_iommu_type1_info_cap_iova_range { pub header: vfio_info_cap_header, pub nr_iovas: __u32, pub reserved: __u32, pub iova_ranges: __IncompleteArrayField, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_type1_info_cap_migration { pub header: vfio_info_cap_header, pub flags: __u32, pub pgsize_bitmap: __u64, pub max_dirty_bitmap_size: __u64, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_type1_info_dma_avail { pub header: vfio_info_cap_header, pub avail: __u32, } #[doc = " VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map)"] #[doc = ""] #[doc = " Map process virtual addresses to IO virtual addresses using the"] #[doc = " provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required."] #[doc = ""] #[doc = " If flags & VFIO_DMA_MAP_FLAG_VADDR, update the base vaddr for iova, and"] #[doc = " unblock translation of host virtual addresses in the iova range. The vaddr"] #[doc = " must have previously been invalidated with VFIO_DMA_UNMAP_FLAG_VADDR. To"] #[doc = " maintain memory consistency within the user application, the updated vaddr"] #[doc = " must address the same memory object as originally mapped. Failure to do so"] #[doc = " will result in user memory corruption and/or device misbehavior. iova and"] #[doc = " size must match those in the original MAP_DMA call. Protection is not"] #[doc = " changed, and the READ & WRITE flags must be 0."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_type1_dma_map { pub argsz: __u32, pub flags: __u32, pub vaddr: __u64, pub iova: __u64, pub size: __u64, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct vfio_bitmap { pub pgsize: __u64, pub size: __u64, pub data: *mut __u64, } impl Default for vfio_bitmap { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() } } } #[doc = " VFIO_IOMMU_UNMAP_DMA - _IOWR(VFIO_TYPE, VFIO_BASE + 14,"] #[doc = "\t\t\t\t\t\t\tstruct vfio_dma_unmap)"] #[doc = ""] #[doc = " Unmap IO virtual addresses using the provided struct vfio_dma_unmap."] #[doc = " Caller sets argsz. The actual unmapped size is returned in the size"] #[doc = " field. No guarantee is made to the user that arbitrary unmaps of iova"] #[doc = " or size different from those used in the original mapping call will"] #[doc = " succeed."] #[doc = ""] #[doc = " VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP should be set to get the dirty bitmap"] #[doc = " before unmapping IO virtual addresses. When this flag is set, the user must"] #[doc = " provide a struct vfio_bitmap in data[]. User must provide zero-allocated"] #[doc = " memory via vfio_bitmap.data and its size in the vfio_bitmap.size field."] #[doc = " A bit in the bitmap represents one page, of user provided page size in"] #[doc = " vfio_bitmap.pgsize field, consecutively starting from iova offset. Bit set"] #[doc = " indicates that the page at that offset from iova is dirty. A Bitmap of the"] #[doc = " pages in the range of unmapped size is returned in the user-provided"] #[doc = " vfio_bitmap.data."] #[doc = ""] #[doc = " If flags & VFIO_DMA_UNMAP_FLAG_ALL, unmap all addresses. iova and size"] #[doc = " must be 0. This cannot be combined with the get-dirty-bitmap flag."] #[doc = ""] #[doc = " If flags & VFIO_DMA_UNMAP_FLAG_VADDR, do not unmap, but invalidate host"] #[doc = " virtual addresses in the iova range. Tasks that attempt to translate an"] #[doc = " iova's vaddr will block. DMA to already-mapped pages continues. This"] #[doc = " cannot be combined with the get-dirty-bitmap flag."] #[repr(C)] #[derive(Debug, Default)] pub struct vfio_iommu_type1_dma_unmap { pub argsz: __u32, pub flags: __u32, pub iova: __u64, pub size: __u64, pub data: __IncompleteArrayField<__u8>, } #[doc = " VFIO_IOMMU_DIRTY_PAGES - _IOWR(VFIO_TYPE, VFIO_BASE + 17,"] #[doc = " struct vfio_iommu_type1_dirty_bitmap)"] #[doc = " IOCTL is used for dirty pages logging."] #[doc = " Caller should set flag depending on which operation to perform, details as"] #[doc = " below:"] #[doc = ""] #[doc = " Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_START flag set, instructs"] #[doc = " the IOMMU driver to log pages that are dirtied or potentially dirtied by"] #[doc = " the device; designed to be used when a migration is in progress. Dirty pages"] #[doc = " are logged until logging is disabled by user application by calling the IOCTL"] #[doc = " with VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP flag."] #[doc = ""] #[doc = " Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP flag set, instructs"] #[doc = " the IOMMU driver to stop logging dirtied pages."] #[doc = ""] #[doc = " Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP flag set"] #[doc = " returns the dirty pages bitmap for IOMMU container for a given IOVA range."] #[doc = " The user must specify the IOVA range and the pgsize through the structure"] #[doc = " vfio_iommu_type1_dirty_bitmap_get in the data[] portion. This interface"] #[doc = " supports getting a bitmap of the smallest supported pgsize only and can be"] #[doc = " modified in future to get a bitmap of any specified supported pgsize. The"] #[doc = " user must provide a zeroed memory area for the bitmap memory and specify its"] #[doc = " size in bitmap.size. One bit is used to represent one page consecutively"] #[doc = " starting from iova offset. The user should provide page size in bitmap.pgsize"] #[doc = " field. A bit set in the bitmap indicates that the page at that offset from"] #[doc = " iova is dirty. The caller must set argsz to a value including the size of"] #[doc = " structure vfio_iommu_type1_dirty_bitmap_get, but excluding the size of the"] #[doc = " actual bitmap. If dirty pages logging is not enabled, an error will be"] #[doc = " returned."] #[doc = ""] #[doc = " Only one of the flags _START, _STOP and _GET may be specified at a time."] #[doc = ""] #[repr(C)] #[derive(Debug, Default)] pub struct vfio_iommu_type1_dirty_bitmap { pub argsz: __u32, pub flags: __u32, pub data: __IncompleteArrayField<__u8>, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct vfio_iommu_type1_dirty_bitmap_get { pub iova: __u64, pub size: __u64, pub bitmap: vfio_bitmap, } impl Default for vfio_iommu_type1_dirty_bitmap_get { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() } } } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_spapr_tce_ddw_info { pub pgsizes: __u64, pub max_dynamic_windows_supported: __u32, pub levels: __u32, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_spapr_tce_info { pub argsz: __u32, pub flags: __u32, pub dma32_window_start: __u32, pub dma32_window_size: __u32, pub ddw: vfio_iommu_spapr_tce_ddw_info, } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_eeh_pe_err { pub type_: __u32, pub func: __u32, pub addr: __u64, pub mask: __u64, } #[repr(C)] #[derive(Copy, Clone)] pub struct vfio_eeh_pe_op { pub argsz: __u32, pub flags: __u32, pub op: __u32, pub __bindgen_anon_1: vfio_eeh_pe_op__bindgen_ty_1, } #[repr(C)] #[derive(Copy, Clone)] pub union vfio_eeh_pe_op__bindgen_ty_1 { pub err: vfio_eeh_pe_err, } impl Default for vfio_eeh_pe_op__bindgen_ty_1 { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() } } } impl Default for vfio_eeh_pe_op { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); unsafe { ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); s.assume_init() } } } #[doc = " VFIO_IOMMU_SPAPR_REGISTER_MEMORY - _IOW(VFIO_TYPE, VFIO_BASE + 17, struct vfio_iommu_spapr_register_memory)"] #[doc = ""] #[doc = " Registers user space memory where DMA is allowed. It pins"] #[doc = " user pages and does the locked memory accounting so"] #[doc = " subsequent VFIO_IOMMU_MAP_DMA/VFIO_IOMMU_UNMAP_DMA calls"] #[doc = " get faster."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_spapr_register_memory { pub argsz: __u32, pub flags: __u32, pub vaddr: __u64, pub size: __u64, } #[doc = " VFIO_IOMMU_SPAPR_TCE_CREATE - _IOWR(VFIO_TYPE, VFIO_BASE + 19, struct vfio_iommu_spapr_tce_create)"] #[doc = ""] #[doc = " Creates an additional TCE table and programs it (sets a new DMA window)"] #[doc = " to every IOMMU group in the container. It receives page shift, window"] #[doc = " size and number of levels in the TCE table being created."] #[doc = ""] #[doc = " It allocates and returns an offset on a PCI bus of the new DMA window."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_spapr_tce_create { pub argsz: __u32, pub flags: __u32, pub page_shift: __u32, pub __resv1: __u32, pub window_size: __u64, pub levels: __u32, pub __resv2: __u32, pub start_addr: __u64, } #[doc = " VFIO_IOMMU_SPAPR_TCE_REMOVE - _IOW(VFIO_TYPE, VFIO_BASE + 20, struct vfio_iommu_spapr_tce_remove)"] #[doc = ""] #[doc = " Unprograms a TCE table from all groups in the container and destroys it."] #[doc = " It receives a PCI bus offset as a window id."] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_spapr_tce_remove { pub argsz: __u32, pub flags: __u32, pub start_addr: __u64, } pci-driver-0.1.3/src/backends/vfio/containers.rs000064400000000000000000000265631046102023000177240ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use std::alloc::{self, Layout}; use std::collections::{BTreeSet, HashMap}; use std::fmt::Debug; use std::fs::{File, OpenOptions}; use std::io::{self, ErrorKind}; use std::mem; use std::ops::Range; use std::os::unix::io::AsRawFd; use std::os::unix::prelude::RawFd; use crate::backends::vfio::bindings::{ vfio_group_status, vfio_info_cap_header, vfio_iommu_type1_dma_map, vfio_iommu_type1_dma_unmap, vfio_iommu_type1_info, VFIO_TYPE1v2_IOMMU, __IncompleteArrayField, vfio_iommu_type1_info_cap_iova_range, vfio_iommu_type1_info_dma_avail, VFIO_API_VERSION, VFIO_DMA_MAP_FLAG_READ, VFIO_DMA_MAP_FLAG_WRITE, VFIO_GROUP_FLAGS_VIABLE, VFIO_IOMMU_INFO_PGSIZES, VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE, VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL, }; use crate::backends::vfio::ioctl::{ vfio_check_extension, vfio_get_api_version, vfio_group_get_status, vfio_group_set_container, vfio_iommu_get_info, vfio_iommu_map_dma, vfio_iommu_unmap_dma, vfio_set_iommu, }; use crate::iommu::{PciIommu, PciIommuInternal}; use crate::regions::Permissions; /* ---------------------------------------------------------------------------------------------- */ fn open_group(group_number: u32) -> io::Result { // open group let file = OpenOptions::new() .read(true) .write(true) .open(format!("/dev/vfio/{}", group_number))?; // check if group is viable let mut group_status = vfio_group_status { argsz: mem::size_of::() as u32, flags: 0, }; unsafe { vfio_group_get_status(file.as_raw_fd(), &mut group_status)? }; if group_status.flags & VFIO_GROUP_FLAGS_VIABLE == 0 { return Err(io::Error::new( ErrorKind::Other, "Group is not viable; are all devices in the group bound to vfio or unbound?", )); } // success Ok(file) } struct IommuInfo { iova_alignment: usize, max_num_mappings: u32, valid_iova_ranges: Box<[Range]>, } fn get_iommu_info(device_fd: RawFd) -> io::Result { let mut iommu_info = vfio_iommu_type1_info { argsz: mem::size_of::() as u32, flags: 0, iova_pgsizes: 0, cap_offset: 0, }; unsafe { vfio_iommu_get_info(device_fd, &mut iommu_info)? }; // get page size if iommu_info.flags & VFIO_IOMMU_INFO_PGSIZES == 0 { return Err(io::Error::new( ErrorKind::Other, "VFIO didn't report IOMMU mapping alignment requirement", )); } let iova_alignment = 1usize << iommu_info.iova_pgsizes.trailing_zeros(); // ensure there are capabilities if iommu_info.argsz <= mem::size_of::() as u32 { return Err(io::Error::new( ErrorKind::Other, "VFIO reported no IOMMU capabilities", )); } // actual vfio_iommu_type1_info struct is bigger, must re-retrieve it with full argsz let layout = Layout::from_size_align(iommu_info.argsz as usize, 8) .map_err(|_| io::Error::new(ErrorKind::Other, "TODO"))?; let bigger_info = unsafe { alloc::alloc(layout) } as *mut vfio_iommu_type1_info; if bigger_info.is_null() { alloc::handle_alloc_error(layout); } unsafe { *bigger_info = vfio_iommu_type1_info { argsz: iommu_info.argsz, flags: 0, iova_pgsizes: 0, cap_offset: 0, }; } unsafe { vfio_iommu_get_info(device_fd, bigger_info)? }; let mut ranges = get_iommu_cap_iova_ranges(bigger_info)?; // validate and adjust ranges ranges.sort_by_key(|r| r.start); if !ranges.is_empty() && ranges[0].start == 0 { // First valid IOVA is 0x0, which can cause problems with some protocols or hypervisors. // Make the user's life easier by dropping the first page of IOVA space. ranges[0].start = iova_alignment as u64; if ranges[0].start >= ranges[0].end { ranges.remove(0); } } if !ranges.windows(2).all(|r| r[0].end <= r[1].start) { return Err(io::Error::new( ErrorKind::Other, "VFIO reported overlapping IOVA ranges", )); } let valid_iova_ranges = ranges.into_boxed_slice(); let max_num_mappings = get_iommu_dma_avail(bigger_info)?; Ok(IommuInfo { iova_alignment, max_num_mappings, valid_iova_ranges, }) } fn get_iommu_cap( info: *const vfio_iommu_type1_info, id: u32, ) -> io::Result<*const vfio_info_cap_header> { let mut offset = unsafe { *info }.cap_offset as usize; while offset != 0 { let header = unsafe { info.cast::().add(offset).cast::() }; if unsafe { *header }.id as u32 == id { return Ok(header); } offset = unsafe { *header }.next as usize; } Err(io::Error::new( ErrorKind::Other, format!("VFIO did not provide IOMMU capability with ID {}", id), )) } fn get_iommu_cap_iova_ranges(info: *const vfio_iommu_type1_info) -> io::Result>> { let cap = get_iommu_cap(info, VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE)? .cast::(); let ranges = unsafe { (*cap).iova_ranges.as_slice((*cap).nr_iovas as usize) }; let ranges = ranges.iter().map(|range| range.start..range.end).collect(); Ok(ranges) } fn get_iommu_dma_avail(info: *const vfio_iommu_type1_info) -> io::Result { let cap = get_iommu_cap(info, VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL)? .cast::(); Ok(unsafe { (*cap).avail }) } /* ---------------------------------------------------------------------------------------------- */ /// A VFIO container representing an IOMMU context that may contain zero or more VFIO groups. #[derive(Debug)] pub struct VfioContainer { file: File, group_numbers: Box<[u32]>, pub(crate) groups: HashMap, iommu_iova_alignment: usize, iommu_max_num_mappings: u32, iommu_valid_iova_ranges: Box<[Range]>, } impl VfioContainer { /// Creates a new, empty [`VfioContainer`]. /// /// This fails if not all devices in all given groups have been bound to vfio-pci (the VFIO docs /// say "it's also sufficient to only unbind the device from host drivers if a VFIO driver is /// unavailable"). /// /// This fails if any of the groups is already open elsewhere, for instance if another /// [`VfioContainer`] containing one of the groups already currently exists. pub fn new(groups: &[u32]) -> io::Result { // open groups let group_numbers = Vec::from(groups) .into_iter() .collect::>() .into_iter() .collect::>(); let groups: HashMap<_, _> = group_numbers .iter() .map(|&n| Ok((n, open_group(n)?))) .collect::>()?; // create container let file = OpenOptions::new() .read(true) .write(true) .open("/dev/vfio/vfio")?; let fd = file.as_raw_fd(); // check API version if unsafe { vfio_get_api_version(fd)? } != VFIO_API_VERSION as i32 { return Err(io::Error::new( ErrorKind::InvalidInput, "Wrong VFIO_API_VERSION", )); } // check extension if unsafe { vfio_check_extension(fd, VFIO_TYPE1v2_IOMMU as usize)? } != 1 { return Err(io::Error::new(ErrorKind::InvalidInput, "TODO")); } // add groups to container for group_file in groups.values() { unsafe { vfio_group_set_container(group_file.as_raw_fd(), &fd)? }; } // enable IOMMU unsafe { vfio_set_iommu(fd, VFIO_TYPE1v2_IOMMU as usize)? }; // get IOMMU info let iommu_info = get_iommu_info(fd)?; // success Ok(VfioContainer { file, group_numbers, groups, iommu_iova_alignment: iommu_info.iova_alignment, iommu_max_num_mappings: iommu_info.max_num_mappings, iommu_valid_iova_ranges: iommu_info.valid_iova_ranges, }) } /// The group numbers of the groups this container contains. /// /// In ascending order, without duplicates. pub fn groups(&self) -> &[u32] { &self.group_numbers } /// Returns a thing that lets you manage IOMMU mappings for DMA for all devices in all groups /// that belong to this container. pub fn iommu(&self) -> PciIommu { PciIommu { internal: self } } /// Tries to reset all the PCI functions in all the VFIO groups that `self` refers to. /// /// This requires that the user has "ownership" over all the affected functions / permissions to /// do it. /// /// TODO: Reset granularity might not match container granularity. Will probably need to expose /// reset topology properly eventually. /// /// TODO: Should probably advertise whether this granularity of reset is supported, so the user /// doesn't have to try resetting to find out. pub fn reset(&self) -> io::Result<()> { // TODO: Implement. Err(io::Error::new(ErrorKind::Other, "not yet implemented")) } } impl PciIommuInternal for VfioContainer { fn alignment(&self) -> usize { self.iommu_iova_alignment } fn valid_iova_ranges(&self) -> &[Range] { &self.iommu_valid_iova_ranges } fn max_num_mappings(&self) -> u32 { self.iommu_max_num_mappings } unsafe fn map( &self, iova: u64, size: usize, address: *const u8, device_permissions: Permissions, ) -> io::Result<()> { // map region let flags = match device_permissions { Permissions::Read => VFIO_DMA_MAP_FLAG_READ, Permissions::Write => VFIO_DMA_MAP_FLAG_WRITE, Permissions::ReadWrite => VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE, }; let dma_map = vfio_iommu_type1_dma_map { argsz: mem::size_of::() as u32, flags, vaddr: address as u64, iova, size: size as u64, }; unsafe { vfio_iommu_map_dma(self.file.as_raw_fd(), &dma_map) }.map_err(|e| { io::Error::new( ErrorKind::Other, format!( "Failed to set up IOMMU mapping process memory [{:#x}, {:#x}) to device \ memory [{:#x}, {:#x}): {}", address as usize, address as usize + size, iova, iova + size as u64, e ), ) })?; // success Ok(()) } fn unmap(&self, iova: u64, size: usize) -> io::Result<()> { let mut dma_unmap = vfio_iommu_type1_dma_unmap { argsz: mem::size_of::() as u32, flags: 0, iova, size: size as u64, data: __IncompleteArrayField::new(), }; unsafe { vfio_iommu_unmap_dma(self.file.as_raw_fd(), &mut dma_unmap)? }; Ok(()) } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/backends/vfio/ioctl.rs000064400000000000000000000064701046102023000166640ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use std::io; use std::os::unix::io::RawFd; use libc::{c_char, c_ulong, ioctl}; use crate::backends::vfio::bindings::{ vfio_device_info, vfio_group_status, vfio_iommu_type1_dma_map, vfio_iommu_type1_dma_unmap, vfio_iommu_type1_info, vfio_irq_info, vfio_irq_set, vfio_region_info, VFIO_BASE, VFIO_TYPE, }; /* ---------------------------------------------------------------------------------------------- */ macro_rules! define_ioctl { ($name:ident, $index:literal) => { pub unsafe fn $name(fd: RawFd) -> io::Result { const CMD: c_ulong = ioctl_cmd($index); let ret = unsafe { ioctl(fd, CMD) }; ioctl_return_to_result(ret) } }; ($name:ident, $index:literal, $arg_name:ident: usize) => { pub unsafe fn $name(fd: RawFd, $arg_name: usize) -> io::Result { const CMD: c_ulong = ioctl_cmd($index); let ret = unsafe { ioctl(fd, CMD, $arg_name) }; ioctl_return_to_result(ret) } }; ($name:ident, $index:literal, $arg_name:ident: $arg_type:ty) => { pub unsafe fn $name(fd: RawFd, $arg_name: $arg_type) -> io::Result { const CMD: c_ulong = ioctl_cmd($index); let ret = unsafe { ioctl(fd, CMD, $arg_name as *const _) }; ioctl_return_to_result(ret) } }; } const fn ioctl_cmd(index: c_ulong) -> c_ulong { const IOC_NRBITS: c_ulong = 8; const IOC_TYPEBITS: c_ulong = 8; const IOC_SIZEBITS: c_ulong = 14; const IOC_NRSHIFT: c_ulong = 0; const IOC_TYPESHIFT: c_ulong = IOC_NRSHIFT + IOC_NRBITS; const IOC_SIZESHIFT: c_ulong = IOC_TYPESHIFT + IOC_TYPEBITS; const IOC_DIRSHIFT: c_ulong = IOC_SIZESHIFT + IOC_SIZEBITS; const IOC_NONE: c_ulong = 0; (IOC_NONE << IOC_DIRSHIFT) | ((VFIO_TYPE as c_ulong) << IOC_TYPESHIFT) | ((VFIO_BASE as c_ulong + index) << IOC_NRSHIFT) | (0 << IOC_SIZESHIFT) } fn ioctl_return_to_result(ret: i32) -> io::Result { if ret >= 0 { Ok(ret) } else { Err(io::Error::last_os_error()) } } /* ---------------------------------------------------------------------------------------------- */ define_ioctl!(vfio_get_api_version, 0); define_ioctl!(vfio_check_extension, 1, extension: usize); define_ioctl!(vfio_set_iommu, 2, iommu_type: usize); define_ioctl!(vfio_group_get_status, 3, status: *mut vfio_group_status); define_ioctl!(vfio_group_set_container, 4, fd: *const i32); define_ioctl!(vfio_group_get_device_fd, 6, address: *const c_char); define_ioctl!(vfio_device_get_info, 7, info: *mut vfio_device_info); define_ioctl!(vfio_device_get_region_info, 8, info: *mut vfio_region_info); define_ioctl!(vfio_device_get_irq_info, 9, info: *mut vfio_irq_info); define_ioctl!(vfio_device_set_irqs, 10, set: *const vfio_irq_set); define_ioctl!(vfio_device_reset, 11); define_ioctl!(vfio_iommu_get_info, 12, info: *mut vfio_iommu_type1_info); define_ioctl!( vfio_iommu_map_dma, 13, info: *const vfio_iommu_type1_dma_map ); define_ioctl!( vfio_iommu_unmap_dma, 14, info: *mut vfio_iommu_type1_dma_unmap ); /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/backends/vfio/mod.rs000064400000000000000000000303231046102023000163230ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ // override the crate-level `deny(unsafe_op_in_unsafe_fn)` #[cfg_attr(feature = "_unsafe-op-in-unsafe-fn", allow(unsafe_op_in_unsafe_fn))] #[allow( dead_code, non_camel_case_types, non_snake_case, non_upper_case_globals )] mod bindings; mod containers; mod ioctl; mod regions; use libc::{mmap64, munmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE}; use std::alloc::{self, Layout}; use std::ffi::CString; use std::fmt::Debug; use std::fs::File; use std::io::{self, ErrorKind}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::prelude::OsStrExt; use std::path::Path; use std::sync::Arc; use std::{mem, ptr}; use crate::backends::vfio::bindings::{ __IncompleteArrayField, vfio_device_info, vfio_irq_info, vfio_irq_set, VFIO_DEVICE_FLAGS_PCI, VFIO_IRQ_INFO_EVENTFD, VFIO_IRQ_SET_ACTION_TRIGGER, VFIO_IRQ_SET_DATA_EVENTFD, VFIO_IRQ_SET_DATA_NONE, VFIO_PCI_BAR0_REGION_INDEX, VFIO_PCI_BAR5_REGION_INDEX, VFIO_PCI_CONFIG_REGION_INDEX, VFIO_PCI_INTX_IRQ_INDEX, VFIO_PCI_MSIX_IRQ_INDEX, VFIO_PCI_MSI_IRQ_INDEX, VFIO_PCI_ROM_REGION_INDEX, }; use crate::backends::vfio::ioctl::{ vfio_device_get_info, vfio_device_get_irq_info, vfio_device_reset, vfio_device_set_irqs, vfio_group_get_device_fd, }; use crate::backends::vfio::regions::{ set_up_bar_or_rom, set_up_config_space, VfioUnmappedPciRegion, }; use crate::config::PciConfig; use crate::device::{PciDevice, PciDeviceInternal}; use crate::interrupts::{PciInterruptKind, PciInterrupts}; use crate::iommu::PciIommu; use crate::regions::{BackedByPciSubregion, OwningPciRegion, Permissions, RegionIdentifier}; pub use containers::VfioContainer; /* ---------------------------------------------------------------------------------------------- */ fn get_device_address>(device_sysfs_path: P) -> io::Result { let path = device_sysfs_path.as_ref().canonicalize()?; let address = path.file_name().unwrap(); Ok(CString::new(address.as_bytes()).unwrap()) } fn get_device_group_number>(device_sysfs_path: P) -> io::Result { let group_sysfs_path = device_sysfs_path .as_ref() .join("iommu_group") .canonicalize()?; let group_dir_name = group_sysfs_path .file_name() .unwrap() .to_str() .ok_or_else(|| io::Error::new(ErrorKind::Other, "TODO"))?; group_dir_name .parse() .map_err(|_| io::Error::new(ErrorKind::Other, "TODO")) } /* ---------------------------------------------------------------------------------------------- */ /// Provides control over a PCI device using VFIO. #[derive(Debug)] pub struct VfioPciDevice { inner: Arc, } impl VfioPciDevice { /// Creates a new [`VfioContainer`] containing only the group that contains the given vfio-pci /// device, then calls [`VfioPciDevice::open_in_container`] with the same path and the created /// container. /// /// Note that this only works if no other [`VfioContainer`] already contains the device's group, /// and so you must use [`VfioPciDevice::open_in_container`] if you want to drive several /// devices from the same VFIO group. pub fn open>(sysfs_path: P) -> io::Result { let group_number = get_device_group_number(&sysfs_path)?; let container = Arc::new(VfioContainer::new(&[group_number])?); Self::open_in_container(sysfs_path, container) } /// Opens a vfio-pci device and adds it to the given container. /// /// `sysfs_path` must correspond to the device's sysfs directory, *e.g.*, /// `/sys/bus/pci/devices/0000:00:01.0`. `container` must contain the group to which the device /// belongs. /// /// Returns a `VfioPciDevice` corresponding to the opened device. pub fn open_in_container>( sysfs_path: P, container: Arc, ) -> io::Result { let device_address = get_device_address(&sysfs_path)?; let group_number = get_device_group_number(&sysfs_path)?; // get group file let group_file = container .groups .get(&group_number) .ok_or_else(|| io::Error::new(ErrorKind::Other, "TODO"))?; // get device file let device_file = unsafe { let fd = vfio_group_get_device_fd(group_file.as_raw_fd(), device_address.as_ptr())?; Arc::new(File::from_raw_fd(fd)) }; // validate device info let mut device_info = vfio_device_info { argsz: mem::size_of::() as u32, flags: 0, num_regions: 0, num_irqs: 0, cap_offset: 0, }; unsafe { vfio_device_get_info(device_file.as_raw_fd(), &mut device_info)? }; if device_info.flags & VFIO_DEVICE_FLAGS_PCI == 0 || device_info.num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1 || device_info.num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1 { return Err(io::Error::new(ErrorKind::Other, "TODO")); } // get interrupt info let get_max_interrupts = |index| { let mut irq_info = vfio_irq_info { argsz: mem::size_of::() as u32, flags: 0, index, count: 0, }; unsafe { vfio_device_get_irq_info(device_file.as_raw_fd(), &mut irq_info)? }; if irq_info.flags & VFIO_IRQ_INFO_EVENTFD == 0 { return Err(io::Error::new(ErrorKind::Other, "TODO")); } Ok(irq_info.count as usize) }; let max_interrupts = [ get_max_interrupts(VFIO_PCI_INTX_IRQ_INDEX)?, get_max_interrupts(VFIO_PCI_MSI_IRQ_INDEX)?, get_max_interrupts(VFIO_PCI_MSIX_IRQ_INDEX)?, ]; // set up config space let config_region = set_up_config_space(&device_file)?; // set up BARs and ROM let bars = (VFIO_PCI_BAR0_REGION_INDEX..=VFIO_PCI_BAR5_REGION_INDEX) .map(|index| set_up_bar_or_rom(&device_file, index)) .collect::>()?; let rom = set_up_bar_or_rom(&device_file, VFIO_PCI_ROM_REGION_INDEX)?; // success Ok(VfioPciDevice { inner: Arc::new(VfioPciDeviceInner { container, file: device_file, config_region, bars, rom, max_interrupts, }), }) } /// Returns a reference to the container to which the device's group belongs. pub fn container(&self) -> &Arc { &self.inner.container } } impl crate::device::Sealed for VfioPciDevice {} impl PciDevice for VfioPciDevice { fn config(&self) -> PciConfig { PciConfig::backed_by(&self.inner.config_region) } fn bar(&self, index: usize) -> Option { let bar = self.inner.bars.get(index)?.as_ref()?; Some(OwningPciRegion::new( Arc::::clone(&self.inner), Arc::::clone(bar), RegionIdentifier::Bar(index), bar.is_mappable(), )) } fn rom(&self) -> Option { let rom = self.inner.rom.as_ref()?; Some(OwningPciRegion::new( Arc::::clone(&self.inner), Arc::::clone(rom), RegionIdentifier::Rom, rom.is_mappable(), )) } fn iommu(&self) -> PciIommu { self.inner.container.iommu() } fn interrupts(&self) -> PciInterrupts { PciInterrupts { device: &*self.inner, } } fn reset(&self) -> io::Result<()> { unsafe { vfio_device_reset(self.inner.file.as_raw_fd())? }; Ok(()) } } /* ---------------------------------------------------------------------------------------------- */ #[derive(Debug)] struct VfioPciDeviceInner { container: Arc, file: Arc, config_region: VfioUnmappedPciRegion, bars: Box<[Option>]>, rom: Option>, max_interrupts: [usize; 3], } impl PciDeviceInternal for VfioPciDeviceInner { // BARs / ROM fn region_map( &self, identifier: RegionIdentifier, offset: u64, len: usize, permissions: Permissions, ) -> io::Result<*mut u8> { let region = match identifier { RegionIdentifier::Bar(index) => &self.bars[index], RegionIdentifier::Rom => &self.rom, }; let region = region.as_ref().unwrap(); let prot_flags = match permissions { Permissions::Read => PROT_READ, Permissions::Write => PROT_WRITE, Permissions::ReadWrite => PROT_READ | PROT_WRITE, }; let address = unsafe { mmap64( ptr::null_mut(), len, prot_flags, MAP_SHARED, self.file.as_raw_fd(), region.offset_in_device_file() as i64 + offset as i64, ) }; if address == MAP_FAILED { Err(io::Error::last_os_error()) } else { Ok(address.cast()) } } unsafe fn region_unmap(&self, _identifier: RegionIdentifier, address: *mut u8, size: usize) { let result = if unsafe { munmap(address.cast(), size) } == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }; // TODO: Do something other than crash on failure? result.unwrap(); } // Interrupts fn interrupts_max(&self, kind: PciInterruptKind) -> usize { self.max_interrupts[kind as usize] } fn interrupts_enable(&self, kind: PciInterruptKind, eventfds: &[RawFd]) -> io::Result<()> { if eventfds.len() > self.max_interrupts[kind as usize] { return Err(io::Error::new(ErrorKind::Other, "TODO")); } // allocate memory for vfio_irq_set let eventfds_size = eventfds.len() * mem::size_of::(); let total_size = mem::size_of::() + eventfds_size; let layout = Layout::from_size_align(total_size, 4) .map_err(|_| io::Error::new(ErrorKind::Other, "TODO"))?; let mem = unsafe { alloc::alloc(layout) }; if mem.is_null() { alloc::handle_alloc_error(layout); } // initialize vfio_irq_set let irq_set = mem as *mut vfio_irq_set; unsafe { (*irq_set).argsz = total_size as u32; (*irq_set).flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; (*irq_set).index = interrupt_index_from_kind(kind); (*irq_set).start = 0; (*irq_set).count = eventfds.len() as u32; } let eventfd_mem_iter = unsafe { (*irq_set) .data .as_mut_slice(eventfds_size) .chunks_exact_mut(4) }; for (mem, eventfd) in eventfd_mem_iter.zip(eventfds) { mem.copy_from_slice(&eventfd.to_ne_bytes()); } // enable interrupt vectors unsafe { vfio_device_set_irqs(self.file.as_raw_fd(), irq_set)? }; Ok(()) } fn interrupts_disable(&self, kind: PciInterruptKind) -> io::Result<()> { let irq_set = vfio_irq_set { argsz: mem::size_of::() as u32, flags: VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, index: interrupt_index_from_kind(kind), start: 0, count: 0, data: __IncompleteArrayField::new(), }; unsafe { vfio_device_set_irqs(self.file.as_raw_fd(), &irq_set)? }; Ok(()) } } fn interrupt_index_from_kind(kind: PciInterruptKind) -> u32 { match kind { PciInterruptKind::Intx => VFIO_PCI_INTX_IRQ_INDEX, PciInterruptKind::Msi => VFIO_PCI_MSI_IRQ_INDEX, PciInterruptKind::MsiX => VFIO_PCI_MSIX_IRQ_INDEX, } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/backends/vfio/regions.rs000064400000000000000000000152161046102023000172160ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use std::fmt::Debug; use std::fs::File; use std::io::{self, ErrorKind}; use std::mem; use std::os::unix::fs::FileExt; use std::os::unix::io::AsRawFd; use std::sync::Arc; use crate::backends::vfio::bindings::{ vfio_region_info, VFIO_PCI_CONFIG_REGION_INDEX, VFIO_REGION_INFO_FLAG_MMAP, VFIO_REGION_INFO_FLAG_READ, VFIO_REGION_INFO_FLAG_WRITE, }; use crate::backends::vfio::ioctl::vfio_device_get_region_info; use crate::regions::{AsPciSubregion, PciRegion, PciSubregion, Permissions}; /* ---------------------------------------------------------------------------------------------- */ #[derive(Debug)] pub struct VfioUnmappedPciRegion { device_file: Arc, offset_in_device_file: u64, length: u64, permissions: Permissions, is_mappable: bool, } impl VfioUnmappedPciRegion { pub(crate) fn offset_in_device_file(&self) -> u64 { self.offset_in_device_file } pub(crate) fn is_mappable(&self) -> bool { self.is_mappable } fn validate_access( &self, required_alignment: u64, offset: u64, length: usize, ) -> io::Result<()> { let end = offset + length as u64; if end > self.length { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Tried to read region range [{:#x}, {:#x}), must be in [0x0, {:#x})", offset, end, self.length ), )); } if offset % required_alignment != 0 || length as u64 % required_alignment != 0 { return Err(io::Error::new( ErrorKind::InvalidInput, format!("Access must be {}-byte aligned", required_alignment), )); } Ok(()) } fn read(&self, required_alignment: u64, offset: u64, buffer: &mut [u8]) -> io::Result<()> { self.validate_access(required_alignment, offset, buffer.len())?; self.device_file .read_exact_at(buffer, self.offset_in_device_file + offset) } fn write(&self, required_alignment: u64, offset: u64, buffer: &[u8]) -> io::Result<()> { self.validate_access(required_alignment, offset, buffer.len())?; self.device_file .write_all_at(buffer, self.offset_in_device_file + offset) } } impl crate::regions::Sealed for VfioUnmappedPciRegion {} impl PciRegion for VfioUnmappedPciRegion { fn len(&self) -> u64 { self.length } fn permissions(&self) -> Permissions { self.permissions } fn as_ptr(&self) -> Option<*const u8> { None } fn as_mut_ptr(&self) -> Option<*mut u8> { None } fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()> { self.read(1, offset, buffer) } fn read_u8(&self, offset: u64) -> io::Result { let mut buffer = [0; 1]; self.read(1, offset, &mut buffer)?; Ok(buffer[0]) } fn write_u8(&self, offset: u64, value: u8) -> io::Result<()> { self.write(1, offset, &[value]) } fn read_le_u16(&self, offset: u64) -> io::Result { let mut buffer = [0; 2]; self.read(2, offset, &mut buffer)?; Ok(u16::from_le_bytes(buffer)) } fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()> { self.write(2, offset, &value.to_le_bytes()) } fn read_le_u32(&self, offset: u64) -> io::Result { let mut buffer = [0; 4]; self.read(4, offset, &mut buffer)?; Ok(u32::from_le_bytes(buffer)) } fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()> { self.write(4, offset, &value.to_le_bytes()) } } impl<'a> AsPciSubregion<'a> for &'a VfioUnmappedPciRegion { fn as_subregion(&self) -> PciSubregion<'a> { let region: &'a dyn PciRegion = *self; <&dyn PciRegion>::as_subregion(®ion) } } /* ---------------------------------------------------------------------------------------------- */ pub(crate) fn set_up_config_space(device_file: &Arc) -> io::Result { let mut region_info = vfio_region_info { argsz: mem::size_of::() as u32, flags: 0, index: VFIO_PCI_CONFIG_REGION_INDEX, cap_offset: 0, size: 0, offset: 0, }; unsafe { vfio_device_get_region_info(device_file.as_raw_fd(), &mut region_info)? }; if region_info.size == 0 { return Err(io::Error::new(ErrorKind::InvalidData, "TODO")); } if region_info.flags & VFIO_REGION_INFO_FLAG_READ == 0 || region_info.flags & VFIO_REGION_INFO_FLAG_WRITE == 0 { return Err(io::Error::new( ErrorKind::InvalidData, "Expected config space to be both readable and writable", )); } let region = VfioUnmappedPciRegion { device_file: Arc::clone(device_file), offset_in_device_file: region_info.offset, length: region_info.size, permissions: Permissions::ReadWrite, is_mappable: false, }; Ok(region) } pub(crate) fn set_up_bar_or_rom( device_file: &Arc, vfio_region_index: u32, ) -> io::Result>> { let mut region_info = vfio_region_info { argsz: mem::size_of::() as u32, flags: 0, index: vfio_region_index, cap_offset: 0, size: 0, offset: 0, }; unsafe { vfio_device_get_region_info(device_file.as_raw_fd(), &mut region_info)? }; if region_info.size == 0 { return Ok(None); // no such region } let readable = region_info.flags & VFIO_REGION_INFO_FLAG_READ != 0; let writable = region_info.flags & VFIO_REGION_INFO_FLAG_WRITE != 0; let permissions = Permissions::new(readable, writable).ok_or_else(|| { io::Error::new( ErrorKind::Other, "Found a region that is neither readable nor writeable", ) })?; let region = VfioUnmappedPciRegion { device_file: Arc::clone(device_file), offset_in_device_file: region_info.offset, length: region_info.size, permissions, is_mappable: region_is_mappable(®ion_info), }; Ok(Some(Arc::new(region))) } fn region_is_mappable(region_info: &vfio_region_info) -> bool { // TODO: Probably not necessary to check if length fits in address space? region_info.flags & VFIO_REGION_INFO_FLAG_MMAP != 0 && region_info.size <= usize::MAX as u64 } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/config/caps.rs000064400000000000000000000471301046102023000152260ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 //! Provides facilities for accessing Capabilities described in the PCI Configuration Space. //! //! For Extended Capabilities, see [`pci_driver::config::ext_caps`](`super::ext_caps`). //! //! The following table relates the section numbers and titles from the "PCI Express® Base //! Specification Revision 6.0" describing Capabilities to the corresponding type: //! //! | Section number | Section title | Type | //! |-|-|-| //! | 7.5.2 | PCI Power Management Capability Structure | [`PciPowerManagementCapability`] | //! | 7.5.3 | PCI Express Capability Structure | [`PciExpressCapability`] | //! | 7.7.1 | MSI Capability Structures | [`MsiCapability`]
[`Msi32BitCapability`]
[`Msi64BitCapability`]
[`Msi32BitPvmCapability`]
[`Msi64BitPvmCapability`] | //! | 7.7.2 | MSI-X Capability and Table Structure | [`MsiXCapability`] | //! | 7.8.5 | Enhanced Allocation Capability Structure (EA) | [`EnhancedAllocationCapability`] | //! | 7.9.4 | Vendor-Specific Capability | [`VendorSpecificCapability`] | //! | 7.9.18 | Vital Product Data Capability (VPD Capability) | [`VitalProductDataCapability`] | //! | 7.9.21 | Conventional PCI Advanced Features Capability (AF) | [`ConventionalPciAdvancedFeaturesCapability`] | //! | 7.9.27 | Null Capability | [`NullCapability`] | /* ---------------------------------------------------------------------------------------------- */ use std::fmt::Debug; use std::io::{self, ErrorKind}; use std::iter::{Flatten, FusedIterator}; use std::marker::PhantomData; use std::ops::Range; use std::vec; use crate::config::PciConfig; use crate::regions::structured::{PciRegisterRo, PciRegisterRw}; use crate::regions::{AsPciSubregion, BackedByPciSubregion, PciRegion, PciSubregion}; use crate::{pci_bit_field, pci_struct}; /* ---------------------------------------------------------------------------------------------- */ /// Some specific type of PCI Capability. pub trait Capability<'a>: PciRegion + AsPciSubregion<'a> + Clone + Copy + Debug + Sized { /// Tries to create an instance of this `Capability` backed by the given [`AsPciSubregion`]. If /// things like for instance the Capablity ID and possibly other factors don't match what is /// expected for the present type, returns `Ok(None)`. /// /// Implementations should also make sure that the subregion is big enough, and fail with an /// error if it isn't. fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> io::Result>; /// The spec doesn't really define a header part explicitly, but this holds the two fields that /// are common to all Capabilities. fn header(&self) -> CapabilityHeader<'a>; } pci_struct! { /// The spec doesn't really define a header part explicitly, but this holds the two fields that /// are common to all Capabilities. pub struct CapabilityHeader<'a> : 0x02 { capability_id @ 0x00 : PciRegisterRo<'a, u8>, /// This field contains the offset to the next PCI Capability structure or 0x00 if no other /// items exist in the linked list of Capabilities. /// /// You don't need to be using this directly. Use [`PciCapabilities`] to iterate over /// capabilities instead. next_capability_pointer @ 0x01 : PciRegisterRo<'a, u8>, } } /* ---------------------------------------------------------------------------------------------- */ /// Lets you inspect and manipulate the PCI Capabilities defined in the configuration space of some /// PCI device. #[derive(Clone, Debug)] pub struct PciCapabilities<'a> { cap_subregions: Box<[PciSubregion<'a>]>, } impl<'a> PciCapabilities<'a> { pub fn backed_by(config_space: PciConfig<'a>) -> io::Result { const CAP_RANGE: Range = 0x40..0x100; // Number of bytes after PCI header and before end of compat config space const ITERATIONS_UPPER_BOUND: usize = CAP_RANGE.end - CAP_RANGE.start; if config_space.len() < 0x100 { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Config space is 0x{:x} bytes long, expected at least 0x100", config_space.len(), ), )); } if !config_space.status().capabilities_list().read()? { // no capabilities pointer return Ok(PciCapabilities { cap_subregions: Box::new([]), }); } let mut cap_subregions = Vec::new(); let mut next_cap_offset = config_space.read_u8(0x34)? & 0xfc; while next_cap_offset != 0x00 { if !CAP_RANGE.contains(&(next_cap_offset as usize)) { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Capability has offset 0x{:02x}, should be in [0x40, 0xff]", next_cap_offset, ), )); } if cap_subregions.len() == ITERATIONS_UPPER_BOUND { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Found more than {} Capabilities, which implies a capability list cycle", ITERATIONS_UPPER_BOUND, ), )); } let cap_subregion = config_space.subregion(next_cap_offset.into()..0x100); let cap_header = CapabilityHeader::backed_by(cap_subregion); cap_subregions.push(cap_subregion); next_cap_offset = cap_header.next_capability_pointer().read()? & 0xfc; } Ok(PciCapabilities { cap_subregions: cap_subregions.into_boxed_slice(), }) } /// Returns an iterator over all capabilities. pub fn iter(&self) -> PciCapabilitiesIter<'a, UnspecifiedCapability<'a>> { // UnspecifiedCapability::backed_by() never fails, so we unwrap() self.of_type().unwrap() } /// Returns an iterator over the capabilities that can be represented by `C`. /// /// This works by trying [`C::backed_by`](Capability::backed_by) on every capability. pub fn of_type>(&self) -> io::Result> { let iter = self .cap_subregions .iter() .map(C::backed_by) .collect::>>()? .into_iter() .flatten(); Ok(PciCapabilitiesIter { iter, phantom: PhantomData, }) } } impl<'a> IntoIterator for PciCapabilities<'a> { type Item = UnspecifiedCapability<'a>; type IntoIter = PciCapabilitiesIntoIter<'a>; fn into_iter(self) -> Self::IntoIter { PciCapabilitiesIntoIter { iter: Vec::from(self.cap_subregions).into_iter(), } } } impl<'a, 'b> IntoIterator for &'b PciCapabilities<'a> { type Item = UnspecifiedCapability<'a>; type IntoIter = PciCapabilitiesIter<'a, UnspecifiedCapability<'a>>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /* ---------------------------------------------------------------------------------------------- */ /// An iterator over all PCI Capabilities of a device. pub struct PciCapabilitiesIntoIter<'a> { iter: vec::IntoIter>, } impl<'a> Iterator for PciCapabilitiesIntoIter<'a> { type Item = UnspecifiedCapability<'a>; fn next(&mut self) -> Option { let subregion = self.iter.next()?; UnspecifiedCapability::backed_by(subregion).unwrap() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl FusedIterator for PciCapabilitiesIntoIter<'_> {} /* ---------------------------------------------------------------------------------------------- */ /// An iterator over a device's PCI Capabilities of a certain type. pub struct PciCapabilitiesIter<'a, C: Capability<'a>> { iter: Flatten>>, phantom: PhantomData<&'a ()>, } impl<'a, C: Capability<'a>> Iterator for PciCapabilitiesIter<'a, C> { type Item = C; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, C: Capability<'a>> FusedIterator for PciCapabilitiesIter<'a, C> {} /* ---------------------------------------------------------------------------------------------- */ macro_rules! pci_capability { ( $( $(#[$attr:meta])* $vis:vis struct $name:ident<$lifetime:lifetime> { $(Id = $id:literal,)? $(Matcher = $matcher:expr,)? Length = $length:expr, Fields = { $( $(#[$field_attr:meta])* $field_name:ident @ $field_offset:literal : $($field_type:ident)::+$(<$($field_generics:tt),+ $(,)?>)? ),* $(,)? } $(,)? } )* ) => { $( $(#[$attr])* #[derive(Clone, Copy)] $vis struct $name<$lifetime> { subregion: $crate::regions::PciSubregion<$lifetime>, } impl<'a> Capability<'a> for $name<'a> { fn backed_by(as_subregion: impl $crate::regions::AsPciSubregion<'a>) -> ::std::io::Result> { let subregion = $crate::regions::AsPciSubregion::as_subregion(&as_subregion); $( let header = $crate::config::caps::CapabilityHeader::backed_by(subregion); if header.capability_id().read()? != $id { return ::std::io::Result::Ok(::std::option::Option::None); } )? // construct capability from a subregion that may be unnecessary long let cap = $name { subregion }; $( let matcher_fn: fn(&Self) -> ::std::io::Result = $matcher; if !matcher_fn(&cap)? { return ::std::io::Result::Ok(::std::option::Option::None); } )? let length_fn: fn(&Self) -> ::std::io::Result = $length; let length: u64 = length_fn(&cap)?.into(); // construct new capability from a subregion with just the right size let cap = $name { subregion: subregion.subregion(..length), }; ::std::io::Result::Ok(::std::option::Option::Some(cap)) } fn header(&self) -> $crate::config::caps::CapabilityHeader<'a> { $crate::regions::BackedByPciSubregion::backed_by(self.subregion) } } impl<'a> $crate::regions::AsPciSubregion<'a> for $name<'a> { fn as_subregion(&self) -> $crate::regions::PciSubregion<'a> { self.subregion } } $crate::_pci_struct_impl! { impl $name<$lifetime> { $( $(#[$field_attr])* $field_name @ $field_offset : $($field_type)::+$(<$($field_generics),+>)? ),* } } )* }; } /* ---------------------------------------------------------------------------------------------- */ pci_capability! { /// Some/any PCI Capability. pub struct UnspecifiedCapability<'a> { Length = |_cap| Ok(0x02), Fields = {}, } } // 7.5.2 PCI Power Management Capability Structure pci_capability! { pub struct PciPowerManagementCapability<'a> { Id = 0x01, Length = |_cap| Ok(0x08), Fields = { // TODO }, } } // 7.5.3 PCI Express Capability Structure pci_capability! { /// TODO: Should take the "Capability Version" into consideration. pub struct PciExpressCapability<'a> { Id = 0x10, Length = |_cap| Ok(0x3c), Fields = { capabilities @ 0x02 : PciExpressCapabilities, device_capabilities @ 0x04 : PciExpressDeviceCapabilities, device_control @ 0x08 : PciExpressDeviceControl, device_status @ 0x0a : PciExpressDeviceStatus, link_capabilities @ 0x0c : PciExpressLinkCapabilities, link_control @ 0x10 : PciExpressLinkControl, link_status @ 0x12 : PciExpressLinkStatus, device_capabilities_2 @ 0x24 : PciExpressDeviceCapabilities2, device_control_2 @ 0x28 : PciExpressDeviceControl2, link_capabilities_2 @ 0x2c : PciExpressLinkCapabilities2, link_control_2 @ 0x30 : PciExpressLinkControl2, link_status_2 @ 0x32 : PciExpressLinkStatus2, }, } } pci_bit_field! { pub struct PciExpressCapabilities<'a> : RO u16 { // TODO } pub struct PciExpressDeviceCapabilities<'a> : RO u32 { max_payload_size_supported @ 0--2 : RO u8, phantom_functions_supported @ 3--4 : RO u8, extended_tag_field_supported @ 5 : RO, endpoint_l0s_acceptable_latency @ 6-- 8 : RO u8, endpoint_l1_acceptable_latency @ 9--11 : RO u8, __ @ 12--14 : RsvdP, role_based_error_reporting @ 15 : RO, err_cor_subclass_capable @ 16 : RO, rx_mps_fixed @ 17 : RO, captured_slot_power_limit_value @ 18--25 : RO u8, captured_slot_power_limit_scale @ 26--27 : RO u8, function_level_reset_capability @ 28 : RO, mixed_mps_supported @ 29 : RO, __ @ 30--31 : RsvdP, } pub struct PciExpressDeviceControl<'a> : RW u16 { // TODO } pub struct PciExpressDeviceStatus<'a> : RW u16 { // TODO } pub struct PciExpressLinkCapabilities<'a> : RO u32 { // TODO } pub struct PciExpressLinkControl<'a> : RW u16 { // TODO } pub struct PciExpressLinkStatus<'a> : RW u16 { // TODO } pub struct PciExpressDeviceCapabilities2<'a> : RO u32 { // TODO } pub struct PciExpressDeviceControl2<'a> : RW u16 { // TODO } pub struct PciExpressLinkCapabilities2<'a> : RO u32 { // TODO } pub struct PciExpressLinkControl2<'a> : RW u16 { // TODO } pub struct PciExpressLinkStatus2<'a> : RW u16 { // TODO } } // 7.7.1 MSI Capability Structures pci_capability! { pub struct MsiCapability<'a> { Id = 0x05, Length = |cap| { let bit_64 = cap.message_control().bit_64_address_capable().read()?; let pvm = cap.message_control().per_vector_masking_capable().read()?; Ok(match (bit_64, pvm) { (false, false) => 0x0c, ( true, false) => 0x10, (false, true) => 0x14, ( true, true) => 0x18, }) }, Fields = { message_control @ 0x02 : MsiMessageControl<'a>, // TODO }, } pub struct Msi32BitCapability<'a> { Id = 0x05, Matcher = |cap| { let bit_64 = cap.message_control().bit_64_address_capable().read()?; let pvm = cap.message_control().per_vector_masking_capable().read()?; Ok(!bit_64 && !pvm) }, Length = |_cap| Ok(0x0c), Fields = { message_control @ 0x02 : MsiMessageControl<'a>, // TODO }, } pub struct Msi64BitCapability<'a> { Id = 0x05, Matcher = |cap| { let bit_64 = cap.message_control().bit_64_address_capable().read()?; let pvm = cap.message_control().per_vector_masking_capable().read()?; Ok(bit_64 && !pvm) }, Length = |_cap| Ok(0x10), Fields = { message_control @ 0x02 : MsiMessageControl<'a>, // TODO }, } pub struct Msi32BitPvmCapability<'a> { Id = 0x05, Matcher = |cap| { let bit_64 = cap.message_control().bit_64_address_capable().read()?; let pvm = cap.message_control().per_vector_masking_capable().read()?; Ok(!bit_64 && pvm) }, Length = |_cap| Ok(0x14), Fields = { message_control @ 0x02 : MsiMessageControl<'a>, // TODO }, } pub struct Msi64BitPvmCapability<'a> { Id = 0x05, Matcher = |cap| { let bit_64 = cap.message_control().bit_64_address_capable().read()?; let pvm = cap.message_control().per_vector_masking_capable().read()?; Ok(bit_64 && pvm) }, Length = |_cap| Ok(0x18), Fields = { message_control @ 0x02 : MsiMessageControl<'a>, // TODO }, } } pci_bit_field! { pub struct MsiMessageControl<'a> : RW u16 { msi_enable @ 0 : RW, multiple_message_capable @ 1--3 : RO u8, multiple_message_enable @ 4--6 : RW u8, bit_64_address_capable @ 7 : RO, per_vector_masking_capable @ 8 : RO, extended_message_data_capable @ 9 : RO, extended_message_data_enable @ 10 : RW, __ @ 11--15 : RsvdP, } } // 7.7.2 MSI-X Capability and Table Structure pci_capability! { pub struct MsiXCapability<'a> { Id = 0x11, Length = |_cap| Ok(0x0c), Fields = { // TODO }, } } // 7.8.5 Enhanced Allocation Capability Structure (EA) pci_capability! { pub struct EnhancedAllocationCapability<'a> { Id = 0x14, Length = |cap| { let num_entries = cap.read_u8(0x02)? & 0x3f; let mut cursor = 0x04; for _ in 0..num_entries { let entry_size = cap.read_u8(cursor.into())? & 0x07; cursor += 1 + entry_size; } Ok(cursor) }, Fields = { // TODO }, } } // 7.9.4 Vendor-Specific Capability pci_capability! { pub struct VendorSpecificCapability<'a> { Id = 0x09, Length = |cap| cap.capability_length().read(), Fields = { capability_length @ 0x02 : PciRegisterRo<'a, u8>, }, } } // 7.9.18 Vital Product Data Capability (VPD Capability) pci_capability! { pub struct VitalProductDataCapability<'a> { Id = 0x03, Length = |_cap| Ok(0x08), Fields = { vpd_address_register @ 0x02 : VpdAddressRegister, vpd_data_register @ 0x04 : PciRegisterRw<'a, u32>, }, } } pci_bit_field! { /// The "VPD Address Register"'s two elements are/can be RW, but we expose them as RO because /// they are supposed to be written only together at once. pub struct VpdAddressRegister<'a> : RW u16 { vpd_address @ 0--14 : RO u16, f @ 15 : RO, } } // 7.9.21 Conventional PCI Advanced Features Capability (AF) pci_capability! { pub struct ConventionalPciAdvancedFeaturesCapability<'a> { Id = 0x13, Length = |_cap| Ok(0x06), Fields = { // TODO }, } } // 7.9.27 Null Capability pci_capability! { pub struct NullCapability<'a> { Id = 0x00, Length = |_cap| Ok(0x02), Fields = {}, } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/config/ext_caps.rs000064400000000000000000000326601046102023000161100ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 //! Provides facilities for accessing Extended Capabilities described in some PCI configuration //! space. //! //! For plain old non-extended Capabilities, see [`pci_driver::config::caps`](`super::caps`). //! //! The following table relates the section numbers and titles from the "PCI Express® Base //! Specification Revision 6.0" describing Extended Capabilities to the corresponding type: //! //! | Section number | Section title | Type | //! |-|-|-| //! | 7.9.5 | Vendor-Specific Extended Capability | [`VendorSpecificExtendedCapability`] | //! | 7.9.28 | Null Extended Capability | [`NullExtendedCapability`] | /* ---------------------------------------------------------------------------------------------- */ use std::fmt::Debug; use std::io::{self, ErrorKind}; use std::iter::{Flatten, FusedIterator}; use std::marker::PhantomData; use std::ops::Range; use std::vec; use crate::config::caps::PciExpressCapability; use crate::config::PciConfig; use crate::pci_bit_field; use crate::regions::{AsPciSubregion, BackedByPciSubregion, PciRegion, PciSubregion}; /* ---------------------------------------------------------------------------------------------- */ /// Some specific type of PCI Extended Capability. pub trait ExtendedCapability<'a>: PciRegion + AsPciSubregion<'a> + Clone + Copy + Debug + Sized { /// Tries to create an instance of this `Capability` backed by the given [`AsPciSubregion`]. If /// things like for instance the Capablity ID and Capability Version and possibly other factors /// don't match what is expected for the present type, returns `Ok(None)`. /// /// Implementations should also make sure that the subregion is big enough, and fail with an /// error if it isn't. fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> io::Result>; /// The header of the Extended Capability. fn header(&self) -> ExtendedCapabilityHeader<'a>; } pci_bit_field! { pub struct ExtendedCapabilityHeader<'a> : RO u32 { capability_id @ 0--15 : RO u16, /// This field is a PCI-SIG defined version number that indicates the version of the /// Capability structure present. capability_version @ 16--19 : RO u8, /// This field contains the offset to the next PCI Express Capability structure or 0x000 if /// no other items exist in the linked list of Capabilities. /// /// You don't need to be using this directly. Use [`PciExtendedCapabilities`] to iterate /// over capabilities instead. next_capability_offset @ 20--31 : RO u16, } } /* ---------------------------------------------------------------------------------------------- */ /// Lets you inspect and manipulate the PCI Extended Capabilities defined in the configuration space /// of some PCI device. #[derive(Clone, Debug)] pub struct PciExtendedCapabilities<'a> { cap_subregions: Box<[PciSubregion<'a>]>, } impl<'a> PciExtendedCapabilities<'a> { pub fn backed_by(config_space: PciConfig<'a>) -> io::Result { const CAP_RANGE: Range = 0x100..0x1000; // Number of 2-byte words in extended config space const ITERATIONS_UPPER_BOUND: usize = (CAP_RANGE.end - CAP_RANGE.start) / 2; if config_space.len() < 0x1000 { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Config space is 0x{:x} bytes long, expected at least 0x1000", config_space.len() ), )); } // This is somewhat expensive, but ensures we don't give unexpected results when the device // is not PCI Express. if config_space .capabilities()? .of_type::()? .next() .is_none() { // not a PCI Express device return Ok(PciExtendedCapabilities { cap_subregions: Box::new([]), }); } let mut cap_subregions = Vec::new(); let mut next_cap_offset = 0x100; // there's always at least one extended capability while next_cap_offset != 0x000 { if !CAP_RANGE.contains(&(next_cap_offset as usize)) { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Extended Capability has offset 0x{:03x}, should be in [0x100, 0xfff]", next_cap_offset, ), )); } if next_cap_offset % 2 != 0 { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Extended Capability has offset 0x{:03x}, expected multiple of two", next_cap_offset, ), )); } if cap_subregions.len() == ITERATIONS_UPPER_BOUND { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Found more than {} Extended Capabilities, which implies a capability \ list cycle", ITERATIONS_UPPER_BOUND, ), )); } let cap_subregion = config_space.subregion(next_cap_offset.into()..0x1000); let cap_header = ExtendedCapabilityHeader::backed_by(cap_subregion); cap_subregions.push(cap_subregion); next_cap_offset = cap_header.next_capability_offset().read()? & 0xfffc; } Ok(PciExtendedCapabilities { cap_subregions: cap_subregions.into_boxed_slice(), }) } /// Returns an iterator over all extended capabilities. pub fn iter(&self) -> PciExtendedCapabilitiesIter<'a, UnspecifiedExtendedCapability<'a>> { // UnspecifiedExtendedCapability::backed_by() never fails, so we unwrap() self.of_type().unwrap() } /// Returns an iterator over the capabilities that can be represented by `C`. /// /// This works by trying [`C::backed_by`](ExtendedCapability::backed_by) on every capability. pub fn of_type>( &self, ) -> io::Result> { let iter = self .cap_subregions .iter() .map(C::backed_by) .collect::>>()? .into_iter() .flatten(); Ok(PciExtendedCapabilitiesIter { iter, phantom: PhantomData, }) } } impl<'a> IntoIterator for PciExtendedCapabilities<'a> { type Item = UnspecifiedExtendedCapability<'a>; type IntoIter = PciExtendedCapabilitiesIntoIter<'a>; fn into_iter(self) -> Self::IntoIter { PciExtendedCapabilitiesIntoIter { iter: Vec::from(self.cap_subregions).into_iter(), } } } impl<'a, 'b> IntoIterator for &'b PciExtendedCapabilities<'a> { type Item = UnspecifiedExtendedCapability<'a>; type IntoIter = PciExtendedCapabilitiesIter<'a, UnspecifiedExtendedCapability<'a>>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /* ---------------------------------------------------------------------------------------------- */ /// An iterator over all PCI Extended Capabilities of a device. pub struct PciExtendedCapabilitiesIntoIter<'a> { iter: vec::IntoIter>, } impl<'a> Iterator for PciExtendedCapabilitiesIntoIter<'a> { type Item = UnspecifiedExtendedCapability<'a>; fn next(&mut self) -> Option { let subregion = self.iter.next()?; UnspecifiedExtendedCapability::backed_by(subregion).unwrap() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl FusedIterator for PciExtendedCapabilitiesIntoIter<'_> {} /* ---------------------------------------------------------------------------------------------- */ /// An iterator over a device's PCI Extended Capabilities of a certain type. pub struct PciExtendedCapabilitiesIter<'a, C: ExtendedCapability<'a>> { iter: Flatten>>, phantom: PhantomData<&'a ()>, } impl<'a, C: ExtendedCapability<'a>> Iterator for PciExtendedCapabilitiesIter<'a, C> { type Item = C; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a, C: ExtendedCapability<'a>> FusedIterator for PciExtendedCapabilitiesIter<'a, C> {} /* ---------------------------------------------------------------------------------------------- */ macro_rules! pci_extended_capability { ( $( $(#[$attr:meta])* $vis:vis struct $name:ident<$lifetime:lifetime> { $(Id = $id:literal,)? $(MinVersion = $min_version:literal,)? $(Matcher = $matcher:expr,)? Length = $length:expr, Fields = { $( $(#[$field_attr:meta])* $field_name:ident @ $field_offset:literal : $($field_type:ident)::+$(<$($field_generics:tt),+ $(,)?>)? ),* $(,)? }, } )* ) => { $( $(#[$attr])* #[derive(Clone, Copy)] $vis struct $name<$lifetime> { subregion: $crate::regions::PciSubregion<$lifetime>, } impl<'a> ExtendedCapability<'a> for $name<'a> { fn backed_by(as_subregion: impl $crate::regions::AsPciSubregion<'a>) -> ::std::io::Result> { let subregion = $crate::regions::AsPciSubregion::as_subregion(&as_subregion); $( let header = $crate::config::ext_caps::ExtendedCapabilityHeader::backed_by(subregion); if header.capability_id().read()? != $id { return ::std::io::Result::Ok(::std::option::Option::None); } )? $( let header = $crate::config::ext_caps::ExtendedCapabilityHeader::backed_by(subregion); if header.capability_version().read()? < $min_version { return ::std::io::Result::Ok(::std::option::Option::None); } )? // construct capability from a subregion that may be unnecessary long let cap = $name { subregion }; $( let matcher_fn: fn(&Self) -> ::std::io::Result<()> = $matcher; if !matcher_fn(&cap)? { return ::std::io::Result::Ok(::std::option::Option::None); } )? let length_fn: fn(&Self) -> ::std::io::Result = $length; let length: u64 = length_fn(&cap)?.into(); // construct new capability from a subregion with just the right size let cap = $name { subregion: subregion.subregion(..length), }; ::std::io::Result::Ok(::std::option::Option::Some(cap)) } fn header(&self) -> $crate::config::ext_caps::ExtendedCapabilityHeader<'a> { $crate::regions::BackedByPciSubregion::backed_by(self.subregion) } } impl<'a> $crate::regions::AsPciSubregion<'a> for $name<'a> { fn as_subregion(&self) -> $crate::regions::PciSubregion<'a> { self.subregion } } $crate::_pci_struct_impl! { impl $name<$lifetime> { $( $(#[$field_attr])* $field_name @ $field_offset : $($field_type)::+$(<$($field_generics),+>)? ),* } } )* }; } /* ---------------------------------------------------------------------------------------------- */ pci_extended_capability! { /// Any PCI Extended Capability. pub struct UnspecifiedExtendedCapability<'a> { Length = |_cap| Ok(0x004), Fields = {}, } } // 7.9.5 Vendor-Specific Extended Capability pci_extended_capability! { /// Described in Section 7.9.5 of the "PCI Express® Base Specification Revision 6.0". pub struct VendorSpecificExtendedCapability<'a> { Id = 0x000b, MinVersion = 0x1, Length = |cap| cap.vendor_specific_header().vsec_length().read(), Fields = { vendor_specific_header @ 0x004 : VendorSpecificHeader, // TODO }, } } pci_bit_field! { /// Described in Section 7.9.5.2 of the "PCI Express® Base Specification Revision 6.0". pub struct VendorSpecificHeader<'a> : RO u32 { vsec_id @ 0--15 : RO u16, vsec_rev @ 16--19 : RO u8, vsec_length @ 20--31 : RO u16, } } // 7.9.28 Null Extended Capability pci_extended_capability! { /// Described in Section 7.9.28 of the "PCI Express® Base Specification Revision 6.0". pub struct NullExtendedCapability<'a> { Id = 0x000b, Length = |_cap| Ok(0x004), Fields = {}, } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/config/mod.rs000064400000000000000000000150031046102023000150510ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ pub mod caps; pub mod ext_caps; use std::io; use crate::config::caps::PciCapabilities; use crate::config::ext_caps::PciExtendedCapabilities; use crate::regions::structured::{PciRegisterRo, PciRegisterRw}; use crate::{pci_bit_field, pci_struct}; /* ---------------------------------------------------------------------------------------------- */ pci_struct! { /// This lets you interact with the config space (conventional or extended) of a PCI device. /// /// This doesn't have a definite length because we want to preserve the `PciSubregion` over the /// whole of config space. pub struct PciConfig<'a> { vendor_id @ 0x00 : PciRegisterRo<'a, u16>, device_id @ 0x02 : PciRegisterRo<'a, u16>, command @ 0x04 : PciCommand<'a>, status @ 0x06 : PciStatus<'a>, revision_id @ 0x08 : PciRegisterRo<'a, u8>, class_code @ 0x09 : PciClassCode<'a>, cache_line_size @ 0x0c : PciRegisterRw<'a, u8>, latency_timer @ 0x0d : PciRegisterRo<'a, u8>, header_type @ 0x0e : PciHeaderType<'a>, bist @ 0x0f : PciBist<'a>, cardbus_cis_pointer @ 0x28 : PciRegisterRo<'a, u32>, subsystem_vendor_id @ 0x2c : PciRegisterRo<'a, u16>, subsystem_id @ 0x2e : PciRegisterRo<'a, u16>, interrupt_line @ 0x3c : PciRegisterRw<'a, u8>, interrupt_pin @ 0x3d : PciRegisterRo<'a, u8>, min_gnt @ 0x3e : PciRegisterRo<'a, u8>, max_lat @ 0x3f : PciRegisterRo<'a, u8>, } } impl<'a> PciConfig<'a> { /// Returns a thing that lets you access the PCI Capabilities. /// /// Calling this will (re)scan all Capabilities, which is why it can fail. pub fn capabilities(&self) -> io::Result> { PciCapabilities::backed_by(*self) } /// Returns a thing that lets you access the PCI Extended Capabilities. /// /// Calling this will (re)scan all Extended Capabilities, which is why it can fail. pub fn extended_capabilities(&self) -> io::Result> { PciExtendedCapabilities::backed_by(*self) } } // 7.5.1.1.3 Command Register pci_bit_field! { pub struct PciCommand<'a> : RW u16 { io_space_enable @ 0 : RW, memory_space_enable @ 1 : RW, bus_master_enable @ 2 : RW, special_cycle_enable @ 3 : RO, memory_write_and_invalidate @ 4 : RO, vga_palette_snoop @ 5 : RO, parity_error_response @ 6 : RW, idsel_stepping_wait_cycle_control @ 7 : RO, serr_enable @ 8 : RW, fast_back_to_back_transactions_enable @ 9 : RO, interrupt_disable @ 10 : RW1C, __ @ 11--15 : RsvdP, } } // 7.5.1.1.4 Status Register pci_bit_field! { pub struct PciStatus<'a> : RW u16 { immediate_readiness @ 0 : RO, __ @ 1--2 : RsvdZ, interrupt_status @ 3 : RO, capabilities_list @ 4 : RO, mhz_66_capable @ 5 : RO, __ @ 6 : RsvdZ, fast_back_to_back_transactions_capable @ 7 : RO, master_data_parity_error @ 8 : RW1C, devsel_timing @ 9--10 : RO u8, signaled_target_abort @ 11 : RW1C, received_target_abort @ 12 : RW1C, received_master_abort @ 13 : RW1C, signaled_system_error @ 14 : RW1C, detected_parity_error @ 15 : RW1C, } } // 7.5.1.1.6 Class Code Register pci_struct! { pub struct PciClassCode<'a> : 0x03 { base_class_code @ 0x00 : PciRegisterRo<'a, u8>, sub_class_code @ 0x01 : PciRegisterRo<'a, u8>, programming_interface @ 0x02 : PciRegisterRo<'a, u8>, } } // 7.5.1.1.9 Header Type Register pci_bit_field! { pub struct PciHeaderType<'a> : RO u8 { header_layout @ 0--6 : RO u8, multi_function_device @ 7 : RO, } } // 7.5.1.1.10 BIST Register pci_bit_field! { pub struct PciBist<'a> : RW u8 { completion_code @ 0--3 : RO u8, __ @ 4--5 : RsvdP, start_bist @ 6 : RW, bist_capable @ 7 : RO, } } /* ---------------------------------------------------------------------------------------------- */ #[cfg(test)] mod tests { use crate::backends::mock::MockPciDevice; use crate::config::caps::Capability; use crate::config::ext_caps::ExtendedCapability; use crate::device::PciDevice; #[test] fn test_lifetimes() { let device: &dyn PciDevice = &MockPciDevice; let value_1 = device.config().command().io_space_enable(); let value_2 = device .config() .capabilities() .unwrap() .iter() .next() .unwrap() .header() .capability_id(); value_1.read().unwrap(); value_2.read().unwrap(); value_1.read().unwrap(); } #[test] fn test_capabilities() { let device: &dyn PciDevice = &MockPciDevice; let cap_ids: Vec<_> = device .config() .capabilities() .unwrap() .iter() .map(|cap| cap.header().capability_id().read().unwrap()) .collect(); assert_eq!(cap_ids, vec![0x01, 0x05, 0x10, 0x11]); } #[test] fn test_extended_capabilities() { let device: &dyn PciDevice = &MockPciDevice; let ext_cap_ids: Vec<_> = device .config() .extended_capabilities() .unwrap() .iter() .map(|cap| cap.header().capability_id().read().unwrap()) .collect(); assert_eq!( ext_cap_ids, vec![0x0001, 0x0003, 0x0004, 0x0019, 0x0018, 0x001e] ); } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/device.rs000064400000000000000000000103601046102023000142650ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use std::fmt::Debug; use std::io; use std::os::unix::io::RawFd; use crate::config::PciConfig; use crate::interrupts::{PciInterruptKind, PciInterrupts}; use crate::iommu::PciIommu; use crate::regions::{OwningPciRegion, Permissions, RegionIdentifier}; /* ---------------------------------------------------------------------------------------------- */ pub(crate) use private::Sealed; mod private { /// Private trait that can be used as a supertrait to make other traits non-implementable from /// outside this crate: https://jack.wrenn.fyi/blog/private-trait-methods/ pub trait Sealed {} } /// Represents a PCI __function__. /// /// This trait is _sealed_ for forward-compatibility reasons, and thus cannot be implemented by /// users of the crate. pub trait PciDevice: Debug + Send + Sync + Sealed { /// Returns a thing that lets you access the PCI configuration space. /// /// The returned value borrows the `PciDevice`. fn config(&self) -> PciConfig; /// Returns a region that corresponds to the Base Address Register (BAR) with the given index, /// or `None` if there is no such BAR or it is unused by the device. /// /// Unused BARs appear as [`None`]. Also, if you want to refer to a 64-bit BAR, use the lower /// index (of the underlying, consecutive 32-bit BARs); the higher index is [`None`] in that /// case. /// /// Note that PCI allows used BARs to be interspersed with unused BARs. Also, 64-bit BARs don't /// need to be "aligned". For instance, it is possible for a device to use BAR 0 as a 32-bit /// BAR, leave BARs 1 and 2 unused, used 3 and 4 for a 64-bit BAR, and use BAR 5 for another /// 32-bit BAR. /// /// The returned value does _not_ borrow the `PciDevice`, instead sharing ownership of its /// internal resources, so take care to drop it when you want to fully let go of the device. fn bar(&self, index: usize) -> Option; /// Returns a region that is the PCI Expansion ROM, or `None` if the device doesn't have one. /// /// The returned value does _not_ borrow the `PciDevice`, instead sharing ownership of its /// internal resources, so take care to drop it when you want to fully let go of the device. fn rom(&self) -> Option; // TODO: Also expose VGA space? /// Returns a thing that lets you manage IOMMU mappings for DMA. /// /// NOTE: Depending on the backend and on how the `PciDevice` was instantiated, this may also /// affect IOMMU mappings for other PCI functions. /// /// The returned value borrows the `PciDevice`. fn iommu(&self) -> PciIommu; /// Returns a thing that lets you manage interrupts. /// /// The returned value borrows the `PciDevice`. fn interrupts(&self) -> PciInterrupts; /// Reset this function, and only it. /// /// This will fail if it would be necessary to reset other functions or devices as well to get /// this one to be reset (probably can only happen with multi-function devices that don't /// support Function-Level Reset). /// /// This can also fail for other unspecified reasons. /// /// TODO: Should probably advertise whether this granularity of reset is supported, so the user /// doesn't have to try resetting to find out. fn reset(&self) -> io::Result<()>; } /* ---------------------------------------------------------------------------------------------- */ pub(crate) trait PciDeviceInternal: Debug + Send + Sync { // BARs / ROM fn region_map( &self, identifier: RegionIdentifier, offset: u64, len: usize, permissions: Permissions, ) -> io::Result<*mut u8>; unsafe fn region_unmap(&self, identifier: RegionIdentifier, address: *mut u8, length: usize); // Interrupts fn interrupts_max(&self, kind: PciInterruptKind) -> usize; fn interrupts_enable(&self, kind: PciInterruptKind, eventfds: &[RawFd]) -> io::Result<()>; fn interrupts_disable(&self, kind: PciInterruptKind) -> io::Result<()>; } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/interrupts.rs000064400000000000000000000056151046102023000152540ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use std::io; use std::os::unix::io::RawFd; use crate::device::PciDeviceInternal; /* ---------------------------------------------------------------------------------------------- */ /// Gives you control over a PCI device's interrupt mechanisms: INTx, MSI, and MSI-X. /// /// Each device may only support a subset of these mechanisms. The [`PciInterruptMechanism::max`] /// method returns 0 for unsupported mechanisms. pub struct PciInterrupts<'a> { pub(crate) device: &'a dyn PciDeviceInternal, } impl PciInterrupts<'_> { /// Returns a thing that gives you control over a PCI device's INTx interrupts. pub fn intx(&self) -> PciInterruptMechanism { PciInterruptMechanism { device_internal: self.device, kind: PciInterruptKind::Intx, } } /// Returns a thing that gives you control over a PCI device's MSI interrupts. pub fn msi(&self) -> PciInterruptMechanism { PciInterruptMechanism { device_internal: self.device, kind: PciInterruptKind::Msi, } } /// Returns a thing that gives you control over a PCI device's MSI-X interrupts. pub fn msi_x(&self) -> PciInterruptMechanism { PciInterruptMechanism { device_internal: self.device, kind: PciInterruptKind::MsiX, } } } /* ---------------------------------------------------------------------------------------------- */ /// Gives you control over a PCI device's specific interrupt mechanism, which may be INTx, MSI, or /// MSI-X. pub struct PciInterruptMechanism<'a> { pub(crate) device_internal: &'a dyn PciDeviceInternal, pub(crate) kind: PciInterruptKind, } impl PciInterruptMechanism<'_> { /// Maximum number of vectors that may be enabled for this particular interrupt mechanism. pub fn max(&self) -> usize { self.device_internal.interrupts_max(self.kind) } /// Enables vectors `0` through `eventfds.len() - 1` of this particular interrupt mechanism. /// /// Fails if `eventfds.len() > self.max()`. pub fn enable(&self, eventfds: &[RawFd]) -> io::Result<()> { self.device_internal.interrupts_enable(self.kind, eventfds) } /// Disables all enabled vectors of this particular interrupt mechanism. pub fn disable(&self) -> io::Result<()> { self.device_internal.interrupts_disable(self.kind) } // TODO: Add interrupt masking? VFIO only supports masking INTx interrupts, though. } /* ---------------------------------------------------------------------------------------------- */ #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum PciInterruptKind { Intx = 0, Msi = 1, MsiX = 2, } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/iommu.rs000064400000000000000000000057041046102023000141620ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use std::io; use std::ops::Range; use crate::regions::Permissions; /* ---------------------------------------------------------------------------------------------- */ /// Represents an IOMMU that controls DMA done by some PCI function, device, or group of devices. /// /// You'll probably need [`std::sync::atomic::fence`] or use types like /// [`AtomicU32`](std::sync::atomic::AtomicU32) somewhere to synchronize accesses properly with the /// device. pub struct PciIommu<'a> { pub(crate) internal: &'a dyn PciIommuInternal, } impl PciIommu<'_> { /// Both `iova` and process `address` must be aligned to this value. /// /// This is always a power of 2, and never less than the system's page size. pub fn alignment(&self) -> usize { self.internal.alignment() } /// IOVA ranges given to [`PciIommu::map`] must be contained in one of the ranges that this /// method returns. pub fn valid_iova_ranges(&self) -> &[Range] { self.internal.valid_iova_ranges() } /// The maximum number of mappings that may be in effect simultaneously. pub fn max_num_mappings(&self) -> u32 { self.internal.max_num_mappings() } /// Add the given mapping to the IOMMU. /// /// - `iova` is the start address of the region in the device's address space. /// - `size` is the length of the region. /// - `address` is a pointer (in the current process' address space) to the start of the region /// to be mapped. /// /// TODO: Alignment constraints? /// /// # Safety /// /// Must make sense. pub unsafe fn map( &self, iova: u64, length: usize, address: *const u8, device_permissions: Permissions, ) -> io::Result<()> { unsafe { self.internal.map(iova, length, address, device_permissions) } } /// Remove the given mapping from the IOMMU. /// /// TODO: Alignment constraints? /// /// Must unmap exactly a full range that was previously mapped using [`PciIommu::map`], or /// several full ranges as long as they are contiguous. Otherwise, this fails. pub fn unmap(&self, iova: u64, size: usize) -> io::Result<()> { self.internal.unmap(iova, size) } } /* ---------------------------------------------------------------------------------------------- */ pub(crate) trait PciIommuInternal { fn alignment(&self) -> usize; fn valid_iova_ranges(&self) -> &[Range]; fn max_num_mappings(&self) -> u32; unsafe fn map( &self, iova: u64, length: usize, address: *const u8, device_permissions: Permissions, ) -> io::Result<()>; fn unmap(&self, iova: u64, length: usize) -> io::Result<()>; } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/lib.rs000064400000000000000000000463441046102023000136070ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 //! A crate for developing user-space PCI and PCIe drivers. //! //! The driver development interface revolves around the [`PciDevice`](device::PciDevice) trait, //! which represents a PCI __function__ and allows you to: //! //! 1. Access its Configuration Space; //! 2. Access the regions defined by its Base Address Registers (BARs); //! 3. Access its Expansion ROM; //! 4. Add and remove mappings from the IOMMU that controls its DMA operations; //! 5. Configure its INTx, MSI, and MSI-X interrupt vectors; //! 6. Reset it. //! //! Implementations of this trait are called _backends_. For now, a single //! [`VfioPciDevice`](backends::vfio::VfioPciDevice) backend is provided, which relies on Linux's //! VFIO driver framework. The availability of this backend can be controlled through the `vfio` //! crate feature. Future backends will each have a corresponding feature. Note that the user cannot //! implement additional backends from outside this crate. //! //! This crate requires Rust 1.47 or above. //! //! The following sections showcase [`PciDevice`](device::PciDevice)'s features. //! //! ## Configuration space //! //! Calling [`PciDevice::config`](device::PciDevice::config) returns a //! [`PciConfig`](config::PciConfig) value, which provides access to the device's configuration //! space. Configuration space is made up of 8-bit, 16-bit, and 32-bit registers. //! //! Each register may represent a single numeric value (_e.g._, "Vendor ID") or be a bit field. Bit //! fields are composed of several independent bits (_e.g._, "Command") or sequences of bits //! (_e.g._, "Status"). In some cases, related registers are organized hierarchically into groups //! (_e.g._, "Class Code"). (The terms being used here might not match exactly the terminology of //! the PCI/PCIe specifications.) //! //! This crate provides "structured" access to each register, bit field, bit sequence, and bit using //! specialized accessor methods, so you don't have to remember details like the offsets of //! registers, masking and shifting for operating on bits, subtleties related to write-1-to-clear //! and reserved-zero bits, etc. //! //! Still, if you really want to, you can bypass all of this and just read and write directly at //! arbitrary offsets of the configuration space. //! //! The API also makes it easy to iterate over Capabilities and Extended Capabilities, and to find //! capabilities with specific Capability IDs, all while providing the same kind of structured //! access interface described above. //! //! Example usage: //! //! ```no_run //! use pci_driver::config::caps::{Capability, PciExpressCapability}; //! use pci_driver::config::ext_caps::{ExtendedCapability, VendorSpecificExtendedCapability}; //! use pci_driver::config::{PciClassCode, PciConfig}; //! use pci_driver::device::PciDevice; //! use pci_driver::regions::{BackedByPciSubregion, PciRegion, PciRegionSnapshot}; //! //! let device: &dyn PciDevice = unimplemented!(); //! //! // Raw config space access //! //! let vendor_id: u16 = device.config().read_le_u16(0x00)?; //! let device_id: u16 = device.config().read_le_u16(0x02)?; //! //! // Structured config space access //! //! let device_id: u16 = device.config().device_id().read()?; //! //! let memory_space_enable: bool = device.config().command().memory_space_enable().read()?; //! device.config().command().memory_space_enable().write(true)?; //! //! device.config().status().master_data_parity_error().clear()?; //! //! let class_code: PciClassCode = device.config().class_code(); //! let base_class_code: u8 = class_code.base_class_code().read()?; //! let sub_class_code: u8 = class_code.sub_class_code().read()?; //! let programming_interface: u8 = class_code.programming_interface().read()?; //! //! // Capabilities //! //! for cap in device.config().capabilities()? { //! // cap has type UnspecifiedCapability //! let cap_id: u8 = cap.header().capability_id().read()?; //! } //! //! let pcie_cap: Option = device //! .config() //! .capabilities()? //! .of_type::()? //! .next(); //! //! if let Some(pcie_cap) = pcie_cap { //! println!("PCI Express device"); //! let supports_flr: bool = pcie_cap //! .device_capabilities() //! .function_level_reset_capability() //! .read()?; //! } else { //! println!("Conventional PCI device"); //! } //! //! // Extended capabilities //! //! for ext_cap in device.config().extended_capabilities()? { //! // cap has type UnspecifiedExtendedCapability //! let cap_id: u16 = ext_cap.header().capability_id().read()?; //! } //! //! let vendor_specific_ext_caps: Vec = device //! .config() //! .extended_capabilities()? //! .of_type::()? //! .collect(); //! //! // Taking snapshot of entire config space, may improve performance if reading many registers //! //! let config_space_snapshot: PciRegionSnapshot = PciRegionSnapshot::take(device.config())?; //! let device_id: u16 = config_space_snapshot.read_le_u16(0x02)?; //! //! let config_space: PciConfig = PciConfig::backed_by(&config_space_snapshot); //! let device_id: u16 = config_space.read_le_u16(0x02)?; //! let device_id: u16 = config_space.device_id().read()?; //! let memory_space_enable: bool = config_space.command().memory_space_enable().read()?; //! //! // Taking snapshot only of a specific capability //! //! let pcie_cap_snapshot: PciRegionSnapshot = PciRegionSnapshot::take( //! config_space //! .capabilities()? //! .of_type::()? //! .next() //! .expect("not a PCIe device") //! )?; //! //! let pcie_cap = PciExpressCapability::backed_by(&pcie_cap_snapshot)?.unwrap(); //! # std::io::Result::Ok(()) //! ``` //! //! ## BARs and Expansion ROM //! //! The [`PciDevice::bar`](device::PciDevice::bar) method can be used to retrieved an //! [`OwningPciRegion`](regions::OwningPciRegion) corresponding to a given Base Address Register //! (BAR) of the device. This value behaves similarly to an instance of //! [`PciConfig`](config::PciConfig), but does not provide the aforementioned "structured access" //! functionality, as BAR contents are device-specific. In addition, //! [`OwningPciRegion`](regions::OwningPciRegion) provides the ability to map the region onto //! process memory (if the region is mappable). //! //! A similar [`PciDevice::rom`](device::PciDevice::rom) method is also provided, giving access to //! the device's "Expansion ROM". //! //! Example usage: //! //! ```no_run //! use pci_driver::device::PciDevice; //! use pci_driver::regions::{MappedOwningPciRegion, OwningPciRegion, PciRegion, PciRegionSnapshot, Permissions}; //! //! let device: &dyn PciDevice = unimplemented!(); //! //! let bar_0: OwningPciRegion = device.bar(0).expect("expected device to have BAR 0"); //! let rom: OwningPciRegion = device.rom().expect("expected device to have Expansion ROM"); //! //! // Non-memory mapped access (always works, may be slower) //! //! assert!(bar_0.permissions().can_read()); //! let value = bar_0.read_le_u32(0x20)?; //! //! // Memory-mapped access using `PciRegion` methods //! //! assert!(bar_0.permissions() == Permissions::ReadWrite); //! assert!(bar_0.is_mappable()); //! let mapped_bar_0: MappedOwningPciRegion = bar_0.map(..4096, Permissions::Read)?; //! //! let value = mapped_bar_0.read_le_u32(0x20)?; //! //! // Memory-mapped access using raw pointers //! //! let value = u32::from_le( //! unsafe { mapped_bar_0.as_ptr().offset(0x20).cast::().read_volatile() } //! ); //! //! // Taking snapshot of BAR 0 //! //! let bar_0_snapshot: PciRegionSnapshot = PciRegionSnapshot::take(&bar_0)?; //! # std::io::Result::Ok(()) //! ``` //! //! See [`pci_struct!` and `pci_bit_field!`](#pci_struct-and-pci_bit_field) further below to see how //! to easily create structured access APIs of your own, which you can use to access BARs and other //! regions with device-specific layouts. //! //! ## IOMMU //! //! The [`PciDevice::iommu`](device::PciDevice::iommu) method returns a //! [`PciIommu`](iommu::PciIommu) value, which can in turn be used to manipulate IOMMU mapping //! affecting the device. //! //! Example usage: //! //! ```no_run //! use pci_driver::device::PciDevice; //! use pci_driver::regions::Permissions; //! //! let device: &dyn PciDevice = unimplemented!(); //! //! let iova: u64 = 0x12345678; //! let region_ptr: *const u8 = unimplemented!(); //! let region_len: usize = 4096; //! //! unsafe { device.iommu().map(iova, region_len, region_ptr, Permissions::ReadWrite) }; //! // ... //! unsafe { device.iommu().unmap(iova, region_len) }; //! # std::io::Result::Ok(()) //! ``` //! //! ## Interrupts //! //! The [`PciDevice::interrupts`](device::PciDevice::interrupts) method returns a //! [`PciInterrupts`](interrupts::PciInterrupts) value, which provides control over the device's //! interrupt vectors. It allows you to associate specific interrupt vectors with eventfd //! descriptors, and to undo that association. //! //! Example usage: //! //! ```no_run //! use std::os::unix::io::RawFd; //! use pci_driver::device::PciDevice; //! //! let device: &dyn PciDevice = unimplemented!(); //! let eventfds: &[RawFd] = unimplemented!(); //! //! let max_enabled_intx_vectors = device.interrupts().intx().max(); //! device.interrupts().intx().enable(eventfds)?; //! device.interrupts().intx().disable()?; //! //! let max_enabled_msi_vectors = device.interrupts().msi().max(); //! device.interrupts().msi().enable(eventfds)?; //! device.interrupts().msi().disable()?; //! //! let max_enabled_msi_x_vectors = device.interrupts().msi_x().max(); //! device.interrupts().msi_x().enable(eventfds)?; //! device.interrupts().msi_x().disable()?; //! # std::io::Result::Ok(()) //! ``` //! //! ## VFIO backend specificities //! //! In the following example, devices 0000:00:01.0 and 0000:00:02.0 belong to VFIO group 42, device //! 0000:00:03.0 to group 123. //! //! ```no_run //! use std::sync::Arc; //! use pci_driver::backends::vfio::{VfioContainer, VfioPciDevice}; //! use pci_driver::device::PciDevice; //! use pci_driver::regions::Permissions; //! //! let container: Arc = Arc::new(VfioContainer::new(&[42, 123])?); //! //! let device_a = VfioPciDevice::open_in_container("/sys/bus/pci/devices/0000:00:01.0", Arc::clone(&container))?; //! let device_b = VfioPciDevice::open_in_container("/sys/bus/pci/devices/0000:00:02.0", Arc::clone(&container))?; //! let device_c = VfioPciDevice::open_in_container("/sys/bus/pci/devices/0000:00:03.0", Arc::clone(&container))?; //! //! unsafe { //! let iova: u64 = 0x12345678; //! let region_ptr: *const u8 = unimplemented!(); //! let region_len: usize = 4096; //! //! // All of the following calls are equivalent. //! //! container.iommu().map(iova, region_len, region_ptr, Permissions::ReadWrite); //! //! device_a.iommu().map(iova, region_len, region_ptr, Permissions::ReadWrite); //! device_b.iommu().map(iova, region_len, region_ptr, Permissions::ReadWrite); //! device_c.iommu().map(iova, region_len, region_ptr, Permissions::ReadWrite); //! } //! //! // Shorthand for when a device is the only one (that we care about) in its group, and the group //! // is the only one in its container //! //! let device = VfioPciDevice::open("/sys/bus/pci/devices/0000:00:01.0")?; //! //! // Resetting a PCI function, which may not be supported //! //! device.reset()?; //! //! // Resetting a whole container, which may also not be supported //! //! device.container().reset()?; //! # std::io::Result::Ok(()) //! ``` //! //! ## `pci_struct!` and `pci_bit_field!` //! //! Many times, your device's BARs or ROM will be structured into registers and bit fields similarly //! to configuration space, or there will be some capabilities that this crate doesn't provide a //! structured access API for, or even the vendor-specific contents of the Vendor Specific //! Capability will have some structure that this crate naturally can't capture. //! //! In C, you would usually cast pointers to overlay a `struct` on the memory corresponding to the //! region you want to access, and then use volatile accesses. You can do similar things in Rust, //! but that is unsafe, only works when the BAR or ROM is memory-mapped, and it can be error-prone //! to build the structs properly due to padding. //! //! A safe alternative is to have `read()` and `write()` functions that take an offset and a length, //! or are parameterized by the type you want to read or write. This crate provides that kind of //! access API through the [`PciRegion`](crate::regions::PciRegion) trait, but using it can be //! cumbersome and it is easy to pass in the wrong offset or read/write the wrong type. Things get //! even worse when you're bit fiddling to manipulate flags and applying write masks to preserve //! some bits, etc. //! //! To solve this, this crate provides the [`pci_struct!`](crate::pci_struct) and //! [`pci_bit_field!`](crate::pci_bit_field) macros, which you can use to easily define //! semantically-aware types that provide structured access to device regions and bit field //! registers. These are also used by the crate itself to define types like //! [`PciConfig`](crate::config::PciConfig) and [`PciStatus`](crate::config::PciStatus). //! //! Take [`PciClassCode`](crate::config::PciClassCode) as an example: //! //! ```no_run //! use pci_driver::pci_struct; //! use pci_driver::regions::structured::PciRegisterRo; //! //! pci_struct! { //! pub struct PciClassCode<'a> : 0x03 { //! base_class_code @ 0x00 : PciRegisterRo<'a, u8>, //! sub_class_code @ 0x01 : PciRegisterRo<'a, u8>, //! programming_interface @ 0x02 : PciRegisterRo<'a, u8>, //! } //! } //! ``` //! //! Values of this type can be created using `PciClassCode::backed_by(subregion)`, where `subregion` //! is anything that implements `AsPciSubregion<'a>`, and the structure is taken to begin at the //! start of that subregion. //! //! Each field follows the format `name @ offset : type` and gives rise to a method with the given //! `name` that returns a value of the given `type`. The `offset` is in bytes from the start of the //! structure. //! //! [`PciConfig`](crate::config::PciConfig)'s definition is another good example: //! //! ```no_run //! use pci_driver::config::{PciClassCode, PciCommand, PciStatus}; //! use pci_driver::pci_struct; //! use pci_driver::regions::structured::PciRegisterRo; //! //! pci_struct! { //! pub struct PciConfig<'a> { //! vendor_id @ 0x00 : PciRegisterRo<'a, u16>, //! device_id @ 0x02 : PciRegisterRo<'a, u16>, //! command @ 0x04 : PciCommand<'a>, //! status @ 0x06 : PciStatus<'a>, //! revision_id @ 0x08 : PciRegisterRo<'a, u8>, //! class_code @ 0x09 : PciClassCode<'a>, //! // ... more fields ... //! } //! } //! ``` //! //! Note that one of the fields is actually of the type we defined above: `PciClassCode`. We also //! specify an offset for it, which will serve as the base offset for the fields that it in turn //! contains. //! //! Note also the "Command" and "Status" fields. These are _bit fields_. Here's how //! [`PciStatus`](crate::config::PciStatus) is defined: //! //! ```no_run //! use pci_driver::pci_bit_field; //! //! pci_bit_field! { //! pub struct PciStatus<'a> : RW u16 { //! immediate_readiness @ 0 : RO, //! __ @ 1--2 : RsvdZ, //! interrupt_status @ 3 : RO, //! capabilities_list @ 4 : RO, //! mhz_66_capable @ 5 : RO, //! __ @ 6 : RsvdZ, //! fast_back_to_back_transactions_capable @ 7 : RO, //! master_data_parity_error @ 8 : RW1C, //! devsel_timing @ 9--10 : RO u8, //! signaled_target_abort @ 11 : RW1C, //! received_target_abort @ 12 : RW1C, //! received_master_abort @ 13 : RW1C, //! signaled_system_error @ 14 : RW1C, //! detected_parity_error @ 15 : RW1C, //! } //! } //! ``` //! //! Values of this type can be created using `PciStatus::backed_by(subregion)`, exactly like types //! defined using `pci_struct!`. The bit field is taken to be at the start of the given subregion. //! //! `PciStatus`'s definition also follows the same general scheme as if using `pci_struct!`, but now //! each line represents a bit or set of bits in a register. First, note the `: RW u16` after the //! struct name: this means that the struct represents a read-write register that is 16 bits wide. //! //! Then, we have the "Immediate Readiness" bit at position 0, i.e., the lowest-order bit in the //! register. It is read-only, hence the `RO`. The format for each bit is `name @ bit : mode`, while //! for sets of more than 1 bit it is `name @ first_bit--last_bit : mode` (`first_bit` and //! `last_bit` are inclusive). //! //! Then we have a `__` line with mode `RsvdZ` that represents 2 consecutive bits. The `RsvdZ` //! terminology comes from the PCI/PCIe specifications, and means that when writing to the register //! as a whole, these bits must always be written as 0. There's also a `RsvdP` mode which means that //! the affected bits must be written exactly how they currently read. (These two modes exist for //! forward-compatibility purposes). //! //! A few lines down, we get to the "Master Data Parity Error" bit, which has mode `RW1C`. This //! means that the bit can be read as usual, and it can be _cleared_ (i.e., made to be 0), but it //! cannot be _set_ (made to be 1), so the bit isn't quite read-write. (RW1C once again comes from //! the PCI/PCIe specifications and approximately stands for Read-or-Write-1-to-Clear.) There's also //! plain `RW` bits, which can be freely read, cleared, and set, and are not showcased in this //! example. //! //! And finally, let's look at "DEVSEL Timing", which occupies bits 9 and 10 and has mode `RO u8`. //! This is a set of two bits which may only be read, not written, and which reads back as an `u8` //! (it could also have been `u16` or `u32`). //! //! In all these cases, the name of the field gives rise to a method that returns a value that //! allows you to inspect (and possibly manipulate) the bit or set of bits. (Note that the `name` of //! the field is ignored for `RsvdZ` and `RsvdP` bits, but it has to be there. It cannot be a single //! `_` as that is not an identifier, so we use `__` instead.) //! //! You don't have to cover every bit in the register, although we do so in the example above. //! Leaving bits unspecified is equivalent to specifying them as `RsvdP`. //! //! Finally, note that when using `pci_struct!` and `pci_bit_field!`, you can add doc comments both //! to the struct or bit field type itself, and to each of their fields or bits. /* ---------------------------------------------------------------------------------------------- */ #![cfg_attr(feature = "_unsafe-op-in-unsafe-fn", deny(unsafe_op_in_unsafe_fn))] #![cfg_attr(not(feature = "_unsafe-op-in-unsafe-fn"), allow(unused_unsafe))] // TODO: enable: // #![warn(missing_docs)] pub mod backends; pub mod config; pub mod device; pub mod interrupts; pub mod iommu; pub mod regions; /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/regions/bit_field_macros.rs000064400000000000000000000273421046102023000177710ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ /// TODO: Document. #[macro_export] macro_rules! pci_bit_field { ( $( $(#[$attr:meta])* $vis:vis struct $name:ident<$lifetime:lifetime> : $mode:ident $type:ty { $( $(#[$elem_attr:meta])* $elem_name:ident @ $elem_first_bit:literal$(--$elem_last_bit:literal)? : $elem_mode:ident $($elem_type:ty)? ),* $(,)? } )* ) => { $( $(#[$attr])* #[derive(Clone, Copy)] $vis struct $name<$lifetime> { region: &$lifetime dyn $crate::regions::PciRegion, offset: u64, } impl<'a> $crate::regions::BackedByPciSubregion<'a> for $name<'a> { fn backed_by(as_subregion: impl $crate::regions::AsPciSubregion<'a>) -> Self { let subregion = $crate::regions::AsPciSubregion::as_subregion(&as_subregion); $name { region: subregion.underlying_region(), offset: subregion.offset_in_underlying_region(), } } } impl<'a> $crate::regions::AsPciSubregion<'a> for $name<'a> { fn as_subregion(&self) -> $crate::regions::PciSubregion<'a> { self.region .subregion(self.offset..self.offset + ::std::mem::size_of::<$type>() as u64) } } impl $crate::regions::structured::PciBitFieldReadable for $name<'_> { type Type = $type; fn read(&self) -> ::std::io::Result<$type> { $crate::regions::structured::PciRegisterValue::read( self.region, self.offset, ) } } impl ::std::fmt::Debug for $name<'_> { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { let mut debug_struct = f.debug_struct(::std::stringify!($name)); $( $crate::_pci_bit_field_debug_elem!( self, debug_struct, $elem_name : $elem_mode $($elem_type)? ); )* debug_struct.finish() } } impl<$lifetime> $name<$lifetime> { $( $crate::_pci_bit_field_elem! { $lifetime $type : $(#[$elem_attr])* $elem_name @ $elem_first_bit$(--$elem_last_bit)? : $elem_mode $($elem_type)? } )* } $crate::_pci_bit_field_impl_writeable_part! { impl $name<$lifetime> : $mode $type { $( $(#[$elem_attr])* $elem_name @ $elem_first_bit$(--$elem_last_bit)? : $elem_mode $($elem_type)? ),* } } )* }; } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _pci_bit_field_debug_elem { ( $self:ident, $debug_struct:ident, $elem_name:ident : RsvdP ) => {}; ( $self:ident, $debug_struct:ident, $elem_name:ident : RsvdZ ) => {}; ( $self:ident, $debug_struct:ident, $elem_name:ident : $elem_mode:ident $($elem_type:ty)? ) => { $debug_struct.field(::std::stringify!($elem_name), &$self.$elem_name()) }; } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _pci_bit_field_impl_writeable_part { ( impl $name:ident<$lifetime:lifetime> : RO $type:ty { $( $(#[$elem_attr:meta])* $elem_name:ident @ $elem_first_bit:literal$(--$elem_last_bit:literal)? : $elem_mode:ident $($elem_type:ty)? ),* $(,)? } ) => {}; ( impl $name:ident<$lifetime:lifetime> : RW $type:ty { $( $(#[$elem_attr:meta])* $elem_name:ident @ $elem_first_bit:literal$(--$elem_last_bit:literal)? : $elem_mode:ident $($elem_type:ty)? ),* $(,)? } ) => { impl $crate::regions::structured::PciBitFieldWriteable for $name<'_> { const WRITE_MASK: $type = $crate::_pci_bit_field_write_mask!( $type, $( @ $elem_first_bit$(--$elem_last_bit)? : $elem_mode $($elem_type)? ),* ); fn write(&self, value: $type) -> ::std::io::Result<()> { $crate::regions::structured::PciRegisterValue::write( value, self.region, self.offset, ) } } } } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _pci_bit_field_elem { ( $lifetime:lifetime $field_type:ty : $(#[$elem_attr:meta])* $elem_name:ident @ $elem_bit:literal : RO ) => { $(#[$elem_attr])* pub fn $elem_name(&self) -> $crate::regions::structured::PciBitReadOnly<$lifetime, $field_type> { $crate::regions::structured::PciBitReadOnly::backed_by( self.region, self.offset, 1 << $elem_bit, // mask ) } }; ( $lifetime:lifetime $field_type:ty : $(#[$elem_attr:meta])* $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RO $elem_type:ty ) => { $(#[$elem_attr])* pub fn $elem_name(&self) -> $crate::regions::structured::PciBitsReadOnly<$lifetime, $field_type, $elem_type> { const MASK: $field_type = $crate::_bit_range!($field_type, $elem_first_bit, $elem_last_bit); $crate::regions::structured::PciBitsReadOnly::backed_by( self.region, self.offset, MASK, $elem_first_bit, // shift ) } }; ( $lifetime:lifetime $field_type:ty : $(#[$elem_attr:meta])* $elem_name:ident @ $elem_bit:literal : RW ) => { $(#[$elem_attr])* pub fn $elem_name(&self) -> $crate::regions::structured::PciBitReadWrite<$lifetime, $field_type> { $crate::regions::structured::PciBitReadWrite::backed_by( self.region, self.offset, 1 << $elem_bit, // mask ::WRITE_MASK, ) } }; ( $lifetime:lifetime $field_type:ty : $(#[$elem_attr:meta])* $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RW $elem_type:ty ) => { $(#[$elem_attr])* pub fn $elem_name(&self) -> $crate::regions::structured::PciBitsReadWrite<$lifetime, $field_type, $elem_type> { const MASK: $field_type = $crate::_bit_range!($field_type, $elem_first_bit, $elem_last_bit); $crate::regions::structured::PciBitsReadWrite::backed_by( self.region, self.offset, MASK, $elem_first_bit, // shift ::WRITE_MASK ) } }; ( $lifetime:lifetime $field_type:ty : $(#[$elem_attr:meta])* $elem_name:ident @ $elem_bit:literal : RW1C ) => { $(#[$elem_attr])* pub fn $elem_name(&self) -> $crate::regions::structured::PciBitReadClear<$lifetime, $field_type> { $crate::regions::structured::PciBitReadClear::backed_by( self.region, self.offset, 1 << $elem_bit, // mask ::WRITE_MASK, ) } }; ( $lifetime:lifetime $field_type:ty : $elem_name:ident @ $elem_bit:literal : RsvdP ) => {}; ( $lifetime:lifetime $field_type:ty : $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RsvdP ) => {}; ( $lifetime:lifetime $field_type:ty : $elem_name:ident @ $elem_bit:literal : RsvdZ ) => {}; ( $lifetime:lifetime $field_type:ty : $elem_name:ident @ $elem_first_bit:literal--$elem_last_bit:literal : RsvdZ ) => {}; } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _pci_bit_field_write_mask { ( $field_type:ty, $( @ $elem_first_bit:literal$(--$elem_last_bit:literal)? : $elem_mode:ident $($elem_type:ty)? ),* $(,)? ) => { $( $crate::_pci_bit_field_write_mask_elem!( $field_type, @ $elem_first_bit$(--$elem_last_bit)? : $elem_mode $($elem_type)? ) & )* !0 }; } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _pci_bit_field_write_mask_elem { ($field_type:ty, @ $elem_bit:literal : RW1C) => {{ !(1 << $elem_bit) }}; ($field_type:ty, @ $elem_bit:literal : RsvdZ) => {{ !(1 << $elem_bit) }}; ($field_type:ty, @ $elem_first_bit:literal--$elem_last_bit:literal : RsvdZ) => {{ !$crate::_bit_range!($field_type, $elem_first_bit, $elem_last_bit) }}; ( $field_type:ty, @ $elem_first_bit:literal$(--$elem_last_bit:literal)? : $elem_mode:ident $($elem_type:ty)? ) => {{ !0 }}; } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _bit_range { ($field_type:ty, $elem_first_bit:literal, $elem_last_bit:literal) => {{ let one: $field_type = 1; let mask_1 = match one.checked_shl($elem_last_bit + 1) { ::std::option::Option::Some(v) => v - 1, ::std::option::Option::None => !0, }; let mask_2 = (1 << $elem_first_bit) - 1; mask_1 & !mask_2 }}; } /* ---------------------------------------------------------------------------------------------- */ #[cfg(test)] mod tests { #[test] fn test_pci_bit_field_write_mask() { assert_eq!( _pci_bit_field_write_mask!( u8, @ 3 : RsvdZ, @ 6--7 : RsvdZ, ), 0b_0011_0111_u8 ); } #[test] fn test_pci_bit_field_write_mask_elem() { assert_eq!( _pci_bit_field_write_mask_elem!(u8, @ 0 : RsvdZ), 0b_1111_1110_u8 ); assert_eq!( _pci_bit_field_write_mask_elem!(u8, @ 3 : RsvdZ), 0b_1111_0111_u8 ); assert_eq!( _pci_bit_field_write_mask_elem!(u8, @ 7 : RsvdZ), 0b_0111_1111_u8 ); assert_eq!( _pci_bit_field_write_mask_elem!(u8, @ 0--1 : RsvdZ), 0b_1111_1100_u8 ); assert_eq!( _pci_bit_field_write_mask_elem!(u8, @ 3--5 : RsvdZ), 0b_1100_0111_u8 ); assert_eq!( _pci_bit_field_write_mask_elem!(u8, @ 6--7 : RsvdZ), 0b_0011_1111_u8 ); } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/regions/mod.rs000064400000000000000000000662461046102023000152710ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 //! Types representing PCI regions (config space, BARs, etc.) and other related types. //! //! ## Base machinery //! //! - [`trait PciRegion`](PciRegion). Sealed. //! - `&'a dyn PciRegion` implements `AsPciSubregion<'a>`, for all `'a`. //! //! - [`struct PciSubregion<'a>`](PciSubregion). //! - `PciSubregion<'a>` implements `PciRegion`, for all `'a`. //! - `PciSubregion<'a>` implements `AsPciSubregion<'a>`, for all `'a`. //! //! - [`trait AsPciSubregion<'a>`](AsPciSubregion). Unlike `PciRegion`, this trait is not sealed. //! - If `T` implements `AsPciSubregion<'a>`, then `&'b T` implements `AsPciSubregion<'a>`, for //! all `'a`, `'b`, `T`. //! - If `T` implements `AsPciSubregion<'a> + Debug + Send + Sync`, then `T` implements //! `PciRegion`, for all `'a`, `T`. //! //! ## `PciRegion` implementations //! //! - [`struct OwningPciRegion`](OwningPciRegion). A region that somehow owns its backing resources, //! and so is not bound by the lifetime of a [`PciDevice`](crate::device::PciDevice). //! - `OwningPciRegion` implements `PciRegion`. //! - `&'a OwningPciRegion` implements `AsPciSubregion<'a>`, for all `'a`. //! //! - [`struct MappedOwningPciRegion`](MappedOwningPciRegion). What you get by calling //! [`OwningPciRegion::map`]. //! - `MappedOwningPciRegion` implements `PciRegion`. //! - `&'a MappedOwningPciRegion` implements `AsPciSubregion<'a>`, for all `'a`. //! //! - [`struct PciMemoryRegion<'a>`](PciMemoryRegion). A region backed by a `&'a [u8]`, `&'a mut //! [u8]`, or raw memory. //! - `PciMemoryRegion<'a>` implements `PciRegion`, for all `'a`. //! - `&'a PciMemoryRegion<'b>` implements `AsPciSubregion<'a>`, for all `'a`, `'b`. //! //! - [`struct PciRegionSnapshot`](PciRegionSnapshot). //! - `PciRegionSnapshot` implements `PciRegion`. //! - `&'a PciRegionSnapshot` implements `AsPciSubregion<'a>`, for all `'a`. //! //! ## And also //! //! - [`trait BackedByPciSubregion<'a>`](BackedByPciSubregion). /* ---------------------------------------------------------------------------------------------- */ mod bit_field_macros; mod struct_macros; pub mod structured; use std::fmt::Debug; use std::io::{self, ErrorKind}; use std::marker::PhantomData; use std::mem; use std::ops::{Bound, Range, RangeBounds}; use std::sync::Arc; use crate::device::PciDeviceInternal; /* ---------------------------------------------------------------------------------------------- */ /// Describes which operations may be performed on some piece of memory or other data region. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Permissions { /// Only reading is allowed. Read, /// Only writing is allowed. Write, /// Both reading and writing are allowed. ReadWrite, } impl Permissions { pub fn new(can_read: bool, can_write: bool) -> Option { match (can_read, can_write) { (false, false) => None, (true, false) => Some(Permissions::Read), (false, true) => Some(Permissions::Write), (true, true) => Some(Permissions::ReadWrite), } } pub fn can_read(&self) -> bool { match self { Permissions::Read => true, Permissions::Write => false, Permissions::ReadWrite => true, } } pub fn can_write(&self) -> bool { match self { Permissions::Read => false, Permissions::Write => true, Permissions::ReadWrite => true, } } } /* ---------------------------------------------------------------------------------------------- */ pub(crate) use private::Sealed; mod private { /// Like [`crate::device::private::Sealed`]. We can't use that same trait here because users /// would be able to indirectly implement it for their own types by implementing /// `AsPciSubregion`, so we define another one with the same name. pub trait Sealed {} } /// A region of PCI Configuration Space, or a BAR, or the Expansion ROM, or VGA Space, or some other /// device region, or maybe something else, as long it is safe to read and write to it concurrently /// with no data races. /// /// The region does not necessarily have RAM semantics, i.e., values can change suddenly, writes /// might not actually write what is being written, reads can have side effects, etc. /// /// Offsets are [`u64`], not [`usize`], so you can operate on 64-bit `PciRegion`s even when /// compiling for 32-bit. /// /// This trait is _sealed_ for forward-compatibility reasons, and thus cannot be implemented by /// users of the crate. #[allow(clippy::len_without_is_empty)] pub trait PciRegion: Debug + Send + Sync + Sealed { /// The length of the region in bytes. fn len(&self) -> u64; /// Whether the region may be read, written, or both. fn permissions(&self) -> Permissions; /// Returns a `const` pointer to the beginning of the `PciRegion`. /// /// If the region is not mapped into memory, this returns `None`. fn as_ptr(&self) -> Option<*const u8>; /// Returns a `mut` pointer to the beginning of the `PciRegion`. /// /// If the region is not writeable or not mapped into memory, this returns `None`. fn as_mut_ptr(&self) -> Option<*mut u8>; /// Read from a contiguous range of the region into a byte buffer. /// /// There is no guarantee that the access will be atomic in any sense, or terribly efficient. fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()>; /// Read an [`u8`] at the given byte offset from the beginning of the `PciRegion`. /// /// This will fail if `offset + 1 > self.len()`. fn read_u8(&self, offset: u64) -> io::Result; /// Write an [`u8`] at the given byte offset from the beginning of the `PciRegion`. /// /// This will fail if `offset + 1 > self.len()`. fn write_u8(&self, offset: u64, value: u8) -> io::Result<()>; /// Read a little-endian [`u16`] at the given byte offset from the beginning of the `PciRegion`. /// /// The read value will be converted from little-endian to the native endianness before being /// returned. /// /// This will fail if `offset + 2 > self.len()`, or if the region requires aligned accesses and /// `offset` is not 2-byte aligned. fn read_le_u16(&self, offset: u64) -> io::Result; /// Write a little-endian [`u16`] at the given byte offset from the beginning of the /// `PciRegion`. /// /// The value will be converted from the native endianness to little-endian before being /// written. /// /// This will fail if `offset + 2 > self.len()`, or if the region requires aligned accesses and /// `offset` is not 2-byte aligned. fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()>; /// Read a little-endian [`u32`] at the given byte offset from the beginning of the `PciRegion`. /// /// The read value will be converted from little-endian to the native endianness before being /// returned. /// /// This will fail if `offset + 4 > self.len()`, or if the region requires aligned accesses and /// `offset` is not 4-byte aligned. fn read_le_u32(&self, offset: u64) -> io::Result; /// Write a little-endian [`u32`] at the given byte offset from the beginning of the /// `PciRegion`. /// /// The value will be converted from the native endianness to little-endian before being /// written. /// /// This will fail if `offset + 4 > self.len()`, or if the region requires aligned accesses and /// `offset` is not 4-byte aligned. fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()>; } /// Implements [`PciRegion`] for the given type `T` by delegating all methods to the existing /// implementation of [`PciRegion`] for `&T`. macro_rules! impl_delegating_pci_region { ($type:ty) => { impl $crate::regions::Sealed for $type {} impl $crate::regions::PciRegion for $type { fn len(&self) -> u64 { $crate::regions::PciRegion::len(&self) } fn permissions(&self) -> $crate::regions::Permissions { $crate::regions::PciRegion::permissions(&self) } fn as_ptr(&self) -> ::std::option::Option<*const u8> { $crate::regions::PciRegion::as_ptr(&self) } fn as_mut_ptr(&self) -> ::std::option::Option<*mut u8> { $crate::regions::PciRegion::as_mut_ptr(&self) } fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> ::std::io::Result<()> { $crate::regions::PciRegion::read_bytes(&self, offset, buffer) } fn read_u8(&self, offset: u64) -> ::std::io::Result { $crate::regions::PciRegion::read_u8(&self, offset) } fn write_u8(&self, offset: u64, value: u8) -> ::std::io::Result<()> { $crate::regions::PciRegion::write_u8(&self, offset, value) } fn read_le_u16(&self, offset: u64) -> ::std::io::Result { $crate::regions::PciRegion::read_le_u16(&self, offset) } fn write_le_u16(&self, offset: u64, value: u16) -> ::std::io::Result<()> { $crate::regions::PciRegion::write_le_u16(&self, offset, value) } fn read_le_u32(&self, offset: u64) -> ::std::io::Result { $crate::regions::PciRegion::read_le_u32(&self, offset) } fn write_le_u32(&self, offset: u64, value: u32) -> ::std::io::Result<()> { $crate::regions::PciRegion::write_le_u32(&self, offset, value) } } }; } /* ---------------------------------------------------------------------------------------------- */ /// A contiguous part of a [`PciRegion`], which is itself also a `PciRegion`. /// /// Simply redirects accesses to the underlying `PciRegion`, offset by the `PciSubregion`'s offset. /// Also makes sure those accesses don't exceed the `PciSubregion`'s end (offset + length). /// /// Create instances of this by calling [`AsPciSubregion::subregion`] on anything that implements /// it, for instance a [`&dyn PciRegion`](PciRegion) or [`OwningPciRegion`]. #[derive(Clone, Copy, Debug)] pub struct PciSubregion<'a> { region: &'a dyn PciRegion, offset: u64, length: u64, } impl<'a> PciSubregion<'a> { pub fn underlying_region(&self) -> &'a dyn PciRegion { self.region } pub fn offset_in_underlying_region(&self) -> u64 { self.offset } fn validate_access(&self, offset: u64, len: usize) -> io::Result<()> { let len = len as u64; if offset + len > self.length { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Tried to access region range [{:#x}, {:#x}), must be within [0x0, {:#x})", offset, offset + len, self.length ), )); } Ok(()) } } /* ---------------------------------------------------------------------------------------------- */ /// For when it is possible to obtain a [`PciSubregion`] representation of a value cheaply. /// /// Also provides a handy [`AsPciSubregion::subregion`] method with a default implementation. pub trait AsPciSubregion<'a> { /// Returns a [`PciSubregion`] corresponding to `self`. fn as_subregion(&self) -> PciSubregion<'a>; /// Returns a [`PciSubregion`] corresponding to a range of `self`. fn subregion(&self, range: impl RangeBounds) -> PciSubregion<'a> { let subregion = Self::as_subregion(self); let range = clamp_range(range, subregion.len()); PciSubregion { region: subregion.underlying_region(), offset: subregion.offset_in_underlying_region() + range.start, length: range.end - range.start, } } } // If a `T` is `AsPciSubregion<'a>`, then any `&T` is also. impl<'a, 'b, T> AsPciSubregion<'a> for &'b T where T: AsPciSubregion<'a>, { fn as_subregion(&self) -> PciSubregion<'a> { T::as_subregion(*self) } } impl<'a> AsPciSubregion<'a> for &'a dyn PciRegion { fn as_subregion(&self) -> PciSubregion<'a> { PciSubregion { region: *self, offset: 0, length: PciRegion::len(*self), } } } impl<'a> AsPciSubregion<'a> for PciSubregion<'a> { fn as_subregion(&self) -> PciSubregion<'a> { *self } } impl<'a, T> Sealed for T where T: AsPciSubregion<'a> + Debug + Send + Sync {} impl<'a, T> PciRegion for T where T: AsPciSubregion<'a> + Debug + Send + Sync, { fn len(&self) -> u64 { let subregion = T::as_subregion(self); subregion.length } fn permissions(&self) -> Permissions { let subregion = T::as_subregion(self); subregion.region.permissions() } fn as_ptr(&self) -> Option<*const u8> { let subregion = T::as_subregion(self); let ptr = subregion.region.as_ptr()?; // TODO: Can any of this overflow? Some(unsafe { ptr.add(subregion.offset as usize) }) } fn as_mut_ptr(&self) -> Option<*mut u8> { let subregion = T::as_subregion(self); let ptr = subregion.region.as_mut_ptr()?; // TODO: Can any of this overflow? Some(unsafe { ptr.add(subregion.offset as usize) }) } fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()> { let subregion = T::as_subregion(self); subregion.validate_access(offset, buffer.len())?; subregion .region .read_bytes(subregion.offset + offset, buffer) } fn read_u8(&self, offset: u64) -> io::Result { let subregion = T::as_subregion(self); subregion.validate_access(offset, mem::size_of::())?; subregion.region.read_u8(subregion.offset + offset) } fn write_u8(&self, offset: u64, value: u8) -> io::Result<()> { let subregion = T::as_subregion(self); subregion.validate_access(offset, mem::size_of::())?; subregion.region.write_u8(subregion.offset + offset, value) } fn read_le_u16(&self, offset: u64) -> io::Result { let subregion = T::as_subregion(self); subregion.validate_access(offset, mem::size_of::())?; subregion.region.read_le_u16(subregion.offset + offset) } fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()> { let subregion = T::as_subregion(self); subregion.validate_access(offset, mem::size_of::())?; subregion .region .write_le_u16(subregion.offset + offset, value) } fn read_le_u32(&self, offset: u64) -> io::Result { let subregion = T::as_subregion(self); subregion.validate_access(offset, mem::size_of::())?; subregion.region.read_le_u32(subregion.offset + offset) } fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()> { let subregion = T::as_subregion(self); subregion.validate_access(offset, mem::size_of::())?; subregion .region .write_le_u32(subregion.offset + offset, value) } } /* ---------------------------------------------------------------------------------------------- */ #[allow(dead_code)] // for when pci-driver is built with no backends #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum RegionIdentifier { Bar(usize), Rom, } /// This is "owning" in the sense that it doesn't borrow the `PciDevice` it came from. /// /// You can use the read and write methods to access the region. This should always work, but may /// not be the most efficient method to access it. If the region is "mappable", consider mapping it /// into memory using [`OwningPciRegion::map`] and using volatile memory accesses. /// /// For instance, BARs can be I/O Space BARs or Memory Space BARs. In either case, you can access /// them through the [`PciRegion`] methods. In addition, if a BAR is a Memory Space BAR, you can map /// it, which gives you another type implementing [`PciRegion`], but accesses through it should be /// more efficient. You can also obtain a `*const u8` or `*mut u8` from that second [`PciRegion`] /// and use that directly. #[derive(Debug)] pub struct OwningPciRegion { device: Arc, region: Arc, offset: u64, length: u64, identifier: RegionIdentifier, is_mappable: bool, } impl OwningPciRegion { #[allow(dead_code)] // for when pci-driver is built with no backends pub(crate) fn new( device: Arc, region: Arc, identifier: RegionIdentifier, is_mappable: bool, ) -> OwningPciRegion { let offset = 0; let length = region.len(); OwningPciRegion { device, region, offset, length, identifier, is_mappable, } } /// Whether the region can be memory-mapped. /// /// If `false`, [`OwningPciRegion::map`] will always fail. pub fn is_mappable(&self) -> bool { self.is_mappable } /// Like PciSubregion's similar method, but returns an "owning" subregion. pub fn owning_subregion(&self, range: impl RangeBounds) -> OwningPciRegion { let range = clamp_range(range, self.length); OwningPciRegion { device: Arc::clone(&self.device), region: Arc::clone(&self.region), offset: self.offset + range.start, length: range.end - range.start, identifier: self.identifier, is_mappable: self.is_mappable, } } /// Memory-map some range of the region into the current process' address space. pub fn map( &self, range: impl RangeBounds, permissions: Permissions, ) -> io::Result { let range = clamp_range(range, self.region.len()); if range.end - range.start > usize::MAX as u64 { return Err(io::Error::new( ErrorKind::InvalidInput, "Range length exceeds usize::MAX", )); } if (permissions.can_read() && !self.permissions().can_read()) || (permissions.can_write() && !self.permissions().can_write()) { return Err(io::Error::new( ErrorKind::InvalidInput, "Requested incompatible permissions", )); } let length = (range.end - range.start) as usize; let ptr = self.device.region_map( self.identifier, self.offset + range.start, length, permissions, )?; let mapped_region = unsafe { PciMemoryRegion::new_raw(ptr, length, permissions) }; Ok(MappedOwningPciRegion { device: Arc::clone(&self.device), region: mapped_region, identifier: self.identifier, ptr, length, }) } } impl_delegating_pci_region! { OwningPciRegion } impl<'a> AsPciSubregion<'a> for &'a OwningPciRegion { fn as_subregion(&self) -> PciSubregion<'a> { (&*self.region).subregion(self.offset..self.offset + self.length) } } /* ---------------------------------------------------------------------------------------------- */ /// A memory-mapped [`OwningPciRegion`]. This is also a [`PciRegion`]. Dropping this unmaps the /// region. #[derive(Debug)] pub struct MappedOwningPciRegion { device: Arc, region: PciMemoryRegion<'static>, identifier: RegionIdentifier, ptr: *mut u8, length: usize, } unsafe impl Send for MappedOwningPciRegion {} unsafe impl Sync for MappedOwningPciRegion {} #[allow(clippy::len_without_is_empty)] impl MappedOwningPciRegion { // TODO: These three methods shadow PciRegion's. This probably isn't a good idea. /// Returns a constant pointer to the beginning of the memory-mapped region. pub fn as_ptr(&self) -> *const u8 { self.ptr } /// Returns a mutable pointer to the beginning of the memory-mapped region. pub fn as_mut_ptr(&self) -> *mut u8 { self.ptr } /// The length of the region. /// /// Unlike [`PciRegion::len`], returns `usize`. pub fn len(&self) -> usize { self.length } } impl_delegating_pci_region! { MappedOwningPciRegion } impl<'a> AsPciSubregion<'a> for &'a MappedOwningPciRegion { fn as_subregion(&self) -> PciSubregion<'a> { (&self.region).as_subregion() } } impl Drop for MappedOwningPciRegion { fn drop(&mut self) { unsafe { self.device .region_unmap(self.identifier, self.ptr, self.length) }; } } /* ---------------------------------------------------------------------------------------------- */ #[derive(Clone, Copy, Debug)] pub struct PciMemoryRegion<'a> { ptr: *mut u8, length: usize, permissions: Permissions, phantom: PhantomData<&'a ()>, } unsafe impl Send for PciMemoryRegion<'_> {} unsafe impl Sync for PciMemoryRegion<'_> {} impl PciMemoryRegion<'_> { pub fn new(data: &[u8]) -> PciMemoryRegion { PciMemoryRegion { ptr: data.as_ptr() as *mut _, length: data.len(), permissions: Permissions::Read, phantom: PhantomData, } } pub fn new_mut(data: &mut [u8]) -> PciMemoryRegion { PciMemoryRegion { ptr: data.as_mut_ptr(), length: data.len(), permissions: Permissions::ReadWrite, phantom: PhantomData, } } /// # Safety /// /// The returned `PciMemoryRegion` must not outlive the data. pub unsafe fn new_raw<'a>( data: *mut u8, length: usize, permissions: Permissions, ) -> PciMemoryRegion<'a> { PciMemoryRegion { ptr: data, length, permissions, phantom: PhantomData, } } fn get_ptr(&self, offset: u64) -> io::Result<*mut T> { // TODO: Handle overflow. let size = std::mem::size_of::() as u64; if offset + size > self.length as u64 { return Err(io::Error::new( ErrorKind::InvalidInput, "Access falls outside region", )); } if offset % size != 0 { return Err(io::Error::new(ErrorKind::InvalidInput, "Unaligned access")); } Ok(unsafe { self.ptr.add(offset as usize).cast::() }) } } impl Sealed for PciMemoryRegion<'_> {} impl PciRegion for PciMemoryRegion<'_> { fn len(&self) -> u64 { self.length as u64 } fn permissions(&self) -> Permissions { self.permissions } fn as_ptr(&self) -> Option<*const u8> { Some(self.ptr) } fn as_mut_ptr(&self) -> Option<*mut u8> { Some(self.ptr) } fn read_bytes(&self, offset: u64, buffer: &mut [u8]) -> io::Result<()> { let end = offset + buffer.len() as u64; if end > self.length as u64 { return Err(io::Error::new( ErrorKind::InvalidInput, format!( "Invalid configuration space range [{:#x}, {:#x}), must be within [0x0, {:#x})", offset, end, self.len() ), )); } // TODO: Will these 1-byte accesses always work? for (off, byte) in (offset..).zip(buffer) { *byte = unsafe { self.get_ptr::(off)?.read_volatile() }; } Ok(()) } fn read_u8(&self, offset: u64) -> io::Result { let v = unsafe { self.get_ptr::(offset)?.read_volatile() }; Ok(v) } fn write_u8(&self, offset: u64, value: u8) -> io::Result<()> { unsafe { self.get_ptr::(offset)?.write_volatile(value) }; Ok(()) } fn read_le_u16(&self, offset: u64) -> io::Result { let v = unsafe { self.get_ptr::(offset)?.read_volatile() }; Ok(u16::from_le(v)) } fn write_le_u16(&self, offset: u64, value: u16) -> io::Result<()> { unsafe { self.get_ptr::(offset)?.write_volatile(value.to_le()) }; Ok(()) } fn read_le_u32(&self, offset: u64) -> io::Result { let v = unsafe { self.get_ptr::(offset)?.read_volatile() }; Ok(u32::from_le(v)) } fn write_le_u32(&self, offset: u64, value: u32) -> io::Result<()> { unsafe { self.get_ptr::(offset)?.write_volatile(value.to_le()) }; Ok(()) } } impl<'a> AsPciSubregion<'a> for &'a PciMemoryRegion<'_> { fn as_subregion(&self) -> PciSubregion<'a> { let region: &dyn PciRegion = *self; <&dyn PciRegion>::as_subregion(®ion) } } /* ---------------------------------------------------------------------------------------------- */ /// Use this to take snapshots of anything that is an [`AsPciSubregion`]. #[derive(Clone, Debug)] pub struct PciRegionSnapshot { buffer: Box<[u8]>, region: PciMemoryRegion<'static>, } impl PciRegionSnapshot { /// Take a snapshot of the given subregion. pub fn take<'a>(as_subregion: impl AsPciSubregion<'a>) -> io::Result { let subregion = as_subregion.as_subregion(); if subregion.len() > isize::MAX as u64 { return Err(io::Error::new(ErrorKind::Other, "TODO")); } let mut buffer = vec![0u8; subregion.len() as usize]; subregion.read_bytes(0, &mut buffer)?; let mut buffer = buffer.into_boxed_slice(); let region = unsafe { PciMemoryRegion::new_raw(buffer.as_mut_ptr(), buffer.len(), Permissions::ReadWrite) }; Ok(PciRegionSnapshot { buffer, region }) } } impl_delegating_pci_region! { PciRegionSnapshot } impl<'a> AsPciSubregion<'a> for &'a PciRegionSnapshot { fn as_subregion(&self) -> PciSubregion<'a> { (&self.region).as_subregion() } } impl From for Box<[u8]> { fn from(snapshot: PciRegionSnapshot) -> Self { snapshot.buffer } } impl From for Vec { fn from(snapshot: PciRegionSnapshot) -> Self { Vec::from(snapshot.buffer) } } /* ---------------------------------------------------------------------------------------------- */ /// Something that is backed by a [`PciSubregion`]. /// /// Types generated by [`pci_struct!`](crate::pci_struct!) and /// [`pci_bit_field!`](crate::pci_bit_field!) implement this. pub trait BackedByPciSubregion<'a> { /// Does not check whether the subregion is big enough. If it isn't, accesses may later fail. fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> Self; } /* ---------------------------------------------------------------------------------------------- */ fn clamp_range(range: impl RangeBounds, max_length: u64) -> Range { let start = match range.start_bound() { Bound::Included(&b) => b, Bound::Excluded(&b) => b + 1, Bound::Unbounded => 0, }; let end = match range.end_bound() { Bound::Included(&b) => b + 1, Bound::Excluded(&b) => b, Bound::Unbounded => max_length, }; Range { start: start.min(max_length), end: end.max(start).min(max_length), } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/regions/struct_macros.rs000064400000000000000000000063301046102023000173660ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ /// TODO: Document. /// /// The optional length is important mostly to make /// [`PciRegionSnapshot`](crate::regions::PciRegionSnapshot) only copy the relevant part instead of /// a lot more. /// /// TODO: Validate field offsets against length. #[macro_export] macro_rules! pci_struct { ( $( $(#[$attr:meta])* $vis:vis struct $name:ident<$lifetime:lifetime> $(: $length:literal)? { $( $(#[$field_attr:meta])* $field_name:ident @ $field_offset:literal : $($field_type:ident)::+$(<$($field_generics:tt),+ $(,)?>)? ),* $(,)? } )* ) => { $( $(#[$attr])* #[derive(Clone, Copy)] $vis struct $name<$lifetime> { subregion: $crate::regions::PciSubregion<$lifetime>, } impl<'a> $crate::regions::BackedByPciSubregion<'a> for $name<'a> { fn backed_by(as_subregion: impl $crate::regions::AsPciSubregion<'a>) -> Self { let subregion = $crate::regions::AsPciSubregion::subregion(&as_subregion, ..$($length)?); $name { subregion } } } impl<'a> $crate::regions::AsPciSubregion<'a> for $name<'a> { fn as_subregion(&self) -> $crate::regions::PciSubregion<'a> { self.subregion } } $crate::_pci_struct_impl! { impl $name<$lifetime> { $( $(#[$field_attr])* $field_name @ $field_offset : $($field_type)::+$(<$($field_generics),+>)? ),* } } )* }; } /// This macro is __internal__. It should __not__ be used outside of the `pci-driver` crate. #[doc(hidden)] #[macro_export] macro_rules! _pci_struct_impl { ( impl $name:ident<$lifetime:lifetime> { $( $(#[$field_attr:meta])* $field_name:ident @ $field_offset:literal : $($field_type:ident)::+$(<$($field_generics:tt),+ $(,)?>)? ),* $(,)? } ) => { impl ::std::fmt::Debug for $name<'_> { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { let mut debug_struct = f.debug_struct(::std::stringify!($name)); $( debug_struct.field(::std::stringify!($field_name), &self.$field_name()); )* debug_struct.finish() } } impl<$lifetime> $name<$lifetime> { $( $(#[$field_attr])* pub fn $field_name(&self) -> $($field_type)::+$(<$($field_generics),+>)? { let subregion = $crate::regions::AsPciSubregion::subregion(self, $field_offset..); $crate::regions::BackedByPciSubregion::backed_by(subregion) } )* } }; } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/src/regions/structured.rs000064400000000000000000000326671046102023000167160ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 /* ---------------------------------------------------------------------------------------------- */ use num_traits::{PrimInt, Unsigned}; use std::convert::TryInto; use std::fmt::{self, Binary, Debug, LowerHex, UpperHex}; use std::io::{self, ErrorKind}; use std::marker::PhantomData; use crate::regions::{AsPciSubregion, BackedByPciSubregion, PciRegion}; /* ---------------------------------------------------------------------------------------------- */ use private::Sealed; mod private { /// Like [`crate::device::private::Sealed`]. pub trait Sealed {} } /// Trait for types that represent the value of a PCI field or register. /// /// This is implemented for [`u8`], [`u16`], and [`u32`]. /// /// This trait is _sealed_, and thus cannot be implemented by users of the crate. pub trait PciRegisterValue: PrimInt + Unsigned + Debug + LowerHex + UpperHex + Binary + Sealed { /// Delegates to [`PciRegion::read_u8`], [`PciRegion::read_le_u16`], or /// [`PciRegion::read_le_u32`]. fn read(region: &dyn PciRegion, offset: u64) -> io::Result; /// Delegates to [`PciRegion::write_u8`], [`PciRegion::write_le_u16`], or /// [`PciRegion::write_le_u32`]. fn write(self, region: &dyn PciRegion, offset: u64) -> io::Result<()>; } impl Sealed for u8 {} impl PciRegisterValue for u8 { fn read(region: &dyn PciRegion, offset: u64) -> io::Result { region.read_u8(offset) } fn write(self, region: &dyn PciRegion, offset: u64) -> io::Result<()> { region.write_u8(offset, self) } } impl Sealed for u16 {} impl PciRegisterValue for u16 { fn read(region: &dyn PciRegion, offset: u64) -> io::Result { region.read_le_u16(offset) } fn write(self, region: &dyn PciRegion, offset: u64) -> io::Result<()> { region.write_le_u16(offset, self) } } impl Sealed for u32 {} impl PciRegisterValue for u32 { fn read(region: &dyn PciRegion, offset: u64) -> io::Result { region.read_le_u32(offset) } fn write(self, region: &dyn PciRegion, offset: u64) -> io::Result<()> { region.write_le_u32(offset, self) } } fn print_debug_hex( value: io::Result, f: &mut fmt::Formatter, ) -> fmt::Result { if let Ok(v) = value { // Avoid newlines around short values, and print in hex since that is usually more useful. write!(f, "Ok({:#x})", v) } else { Debug::fmt(&value, f) } } fn print_debug_bool(value: io::Result, f: &mut fmt::Formatter) -> fmt::Result { if let Ok(v) = value { // Avoid newlines around short values. write!(f, "Ok({})", v) } else { Debug::fmt(&value, f) } } /* ---------------------------------------------------------------------------------------------- */ // READ-ONLY REGISTERS /// An 8-bit, 16-bit, or 32-bit PCI register that is read-only. #[derive(Clone, Copy)] pub struct PciRegisterRo<'a, T: PciRegisterValue> { region: &'a dyn PciRegion, offset: u64, phantom: PhantomData, } impl<'a, T: PciRegisterValue> PciRegisterRo<'a, T> { /// Read the field. pub fn read(&self) -> io::Result { T::read(self.region, self.offset) } } impl<'a, T: PciRegisterValue> BackedByPciSubregion<'a> for PciRegisterRo<'a, T> { fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> Self { let subregion = as_subregion.as_subregion(); PciRegisterRo { region: subregion.underlying_region(), offset: subregion.offset_in_underlying_region(), phantom: PhantomData, } } } impl Debug for PciRegisterRo<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_hex(self.read(), f) } } // READ-WRITE REGISTERS /// An 8-bit, 16-bit, or 32-bit PCI register that is read-write. #[derive(Clone, Copy)] pub struct PciRegisterRw<'a, T: PciRegisterValue> { region: &'a dyn PciRegion, offset: u64, phantom: PhantomData, } impl<'a, T: PciRegisterValue> PciRegisterRw<'a, T> { /// Read the field. pub fn read(&self) -> io::Result { T::read(self.region, self.offset) } /// Write the field. pub fn write(&self, value: T) -> io::Result<()> { value.write(self.region, self.offset) } } impl<'a, T: PciRegisterValue> BackedByPciSubregion<'a> for PciRegisterRw<'a, T> { fn backed_by(as_subregion: impl AsPciSubregion<'a>) -> Self { let subregion = as_subregion.as_subregion(); PciRegisterRw { region: subregion.underlying_region(), offset: subregion.offset_in_underlying_region(), phantom: PhantomData, } } } impl Debug for PciRegisterRw<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_hex(self.read(), f) } } /* ---------------------------------------------------------------------------------------------- */ // BIT FIELD TRAITS /// A PCI register of type that is a bit field and may be read. pub trait PciBitFieldReadable: Debug { /// The type of the register's value. type Type: PciRegisterValue; /// Read the entire bit field at once. fn read(&self) -> io::Result; } /// A PCI register of type that is a bit field and may be written. pub trait PciBitFieldWriteable: PciBitFieldReadable { /// Write mask for the register. /// /// One wants to alter the value of only some bits of the register. However, the register may /// only be read/written in its entirety. Further, some of the bits in the register may need to /// be written with the value 0 in order not to change their actual state in the device. This /// write mask has those bits set to 0, and the others set to 1. Thus, to alter the value of /// only some bits of the register, this must be done: /// /// - Read the register into `x`; /// - Apply the write mask to `x`, _i.e._, `let y = x & WRITE_MASK`; /// - Modify `y` as needed; /// - Write the resulting value into the register. /// /// Of course, you most likely can just use the member functions that this type provides to /// manipulate individual parts of the register, but sometimes you may need to do these steps by /// yourself, _e.g._, to atomically alter several parts of the register at once. const WRITE_MASK: Self::Type; /// Write the entire bit field at once. fn write(&self, value: Self::Type) -> io::Result<()>; } // TODO: Probably make these below use a PciSubregion, so they can check if they are reading/writing // past the end of the region. // READ-ONLY BIT SEQUENCES /// A read-only sequence of bits that is part of a PCI register. #[derive(Clone, Copy)] pub struct PciBitsReadOnly<'a, T, U> where T: PciRegisterValue + TryInto, T::Error: Debug, U: PciRegisterValue, { region: &'a dyn PciRegion, offset: u64, mask: T, shift: u8, phantom: PhantomData, } impl<'a, T, U> PciBitsReadOnly<'a, T, U> where T: PciRegisterValue + TryInto, T::Error: Debug, U: PciRegisterValue, { pub fn backed_by(region: &'a dyn PciRegion, offset: u64, mask: T, shift: u8) -> Self { PciBitsReadOnly { region, offset, mask, shift, phantom: PhantomData, } } /// Read the bit sequence. /// /// This reads the entire register and then masks and shifts the part we're interested in. pub fn read(&self) -> io::Result { let value = (T::read(self.region, self.offset)? & self.mask) >> self.shift.into(); // TODO: Ensure at compile time that this can't fail. Ok(value.try_into().unwrap()) } } impl Debug for PciBitsReadOnly<'_, T, U> where T: PciRegisterValue + TryInto, T::Error: Debug, U: PciRegisterValue, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_hex(self.read(), f) } } // READ-WRITE BIT SEQUENCES /// A read-write sequence of bits that is part of a PCI register. #[derive(Clone, Copy)] pub struct PciBitsReadWrite<'a, T, U> where T: PciRegisterValue + TryInto, T::Error: Debug, U: PciRegisterValue + Into, { region: &'a dyn PciRegion, offset: u64, mask: T, shift: u8, write_mask: T, // must 'and' with this after reading but before altering the bits phantom: PhantomData, } impl<'a, T, U> PciBitsReadWrite<'a, T, U> where T: PciRegisterValue + TryInto, T::Error: Debug, U: PciRegisterValue + Into, { pub fn backed_by( region: &'a dyn PciRegion, offset: u64, mask: T, shift: u8, write_mask: T, ) -> Self { PciBitsReadWrite { region, offset, mask, shift, write_mask, phantom: PhantomData, } } /// Read the bit sequence. /// /// This reads the entire register and then masks and shifts the part we're interested in. pub fn read(&self) -> io::Result { let value = (T::read(self.region, self.offset)? & self.mask) >> self.shift.into(); // TODO: Ensure at compile time that this can't fail. Ok(value.try_into().unwrap()) } /// Write the bit sequence. /// /// This shifts the value and makes sure to not affect any other bits in the underlying /// register. pub fn write(&self, value: U) -> io::Result<()> { let shifted = value.into() << self.shift.into(); if shifted >> self.shift.into() != value.into() || shifted & !self.mask != T::zero() { return Err(io::Error::new(ErrorKind::InvalidInput, "Value is too big")); } let to_write = (T::read(self.region, self.offset)? & self.write_mask) | shifted; to_write.write(self.region, self.offset) } } impl Debug for PciBitsReadWrite<'_, T, U> where T: PciRegisterValue + TryInto, T::Error: Debug, U: PciRegisterValue + Into, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_hex(self.read(), f) } } // READ-ONLY INDIVIDUAL BITS /// A read-only single bit that is part of a PCI register. #[derive(Clone, Copy)] pub struct PciBitReadOnly<'a, T: PciRegisterValue> { region: &'a dyn PciRegion, offset: u64, mask: T, } impl<'a, T: PciRegisterValue> PciBitReadOnly<'a, T> { pub fn backed_by(region: &'a dyn PciRegion, offset: u64, mask: T) -> Self { PciBitReadOnly { region, offset, mask, } } /// Read the bit. /// /// This reads the entire register and then checks the bit we're interested in. pub fn read(&self) -> io::Result { Ok(T::read(self.region, self.offset)? & self.mask != T::zero()) } } impl Debug for PciBitReadOnly<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_bool(self.read(), f) } } // READ-WRITE INDIVIDUAL BITS /// A read-write single bit that is part of a PCI register. #[derive(Clone, Copy)] pub struct PciBitReadWrite<'a, T: PciRegisterValue> { region: &'a dyn PciRegion, offset: u64, mask: T, write_mask: T, // must 'and' with this after reading but before altering the bits } impl<'a, T: PciRegisterValue> PciBitReadWrite<'a, T> { pub fn backed_by(region: &'a dyn PciRegion, offset: u64, mask: T, write_mask: T) -> Self { PciBitReadWrite { region, offset, mask, write_mask, } } /// Read the bit. /// /// This reads the entire register and then checks the bit we're interested in. pub fn read(&self) -> io::Result { Ok(T::read(self.region, self.offset)? & self.mask != T::zero()) } /// Write the bit. /// /// This makes sure to not affect any other bits in the underlying register. pub fn write(&self, value: bool) -> io::Result<()> { let old = T::read(self.region, self.offset)? & self.write_mask; let new = if value { old | self.mask } else { old & !self.mask }; new.write(self.region, self.offset) } } impl Debug for PciBitReadWrite<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_bool(self.read(), f) } } // READ-CLEAR INDIVIDUAL BITS /// A read-clear (RW1C in the spec) single bit that is part of a PCI register. #[derive(Clone, Copy)] pub struct PciBitReadClear<'a, T: PciRegisterValue> { rw: PciBitReadWrite<'a, T>, } impl<'a, T: PciRegisterValue> PciBitReadClear<'a, T> { pub fn backed_by(region: &'a dyn PciRegion, offset: u64, mask: T, write_mask: T) -> Self { PciBitReadClear { rw: PciBitReadWrite { region, offset, mask, write_mask, }, } } /// Read the bit. /// /// This reads the entire register and then checks the bit we're interested in. pub fn read(&self) -> io::Result { self.rw.read() } /// Clear the bit (_i.e._, set it to 0). /// /// This makes sure to not affect any other bits in the underlying register. pub fn clear(&self) -> io::Result<()> { self.rw.write(true) } } impl Debug for PciBitReadClear<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { print_debug_bool(self.read(), f) } } /* ---------------------------------------------------------------------------------------------- */ pci-driver-0.1.3/test.sh000075500000000000000000000026541046102023000132160ustar 00000000000000#!/bin/bash # ---------------------------------------------------------------------------- # set -o errexit -o pipefail -o nounset if (( $# > 1 )); then >&2 echo "Usage: $0 []" exit 2 elif (( $# == 1 )); then rustup=( rustup run -- "$1" ) else rustup=() fi function __log_and_run() { printf '\033[0;33m%s\033[0m\n' "$*" "$@" } function __cargo() { __log_and_run "${rustup[@]}" cargo "$@" } export CARGO_TERM_COLOR=always script_dir="$( dirname "$0" )" if [[ "${script_dir}" != . ]]; then __log_and_run cd "${script_dir}" fi # ---------------------------------------------------------------------------- # function __rust_version_is_at_least() { (echo "min $1"; "${rustup[@]}" rustc --version) | sort -Vk2 | tail -1 | grep -q rustc } if [[ -v PCI_DRIVER_FEATURES ]]; then features=( --no-default-features --features="${PCI_DRIVER_FEATURES}" ) elif __rust_version_is_at_least 1.52; then # feature "_unsafe-op-in-unsafe-fn" only works with Rust 1.52+ features=( --all-features ) else features=() fi __cargo fmt --all -- --check __cargo clippy --all-targets "${features[@]}" -- --deny warnings # this catches problems in doc comments __cargo doc # run doc tests with default features __cargo test --doc # run other tests with requested features __cargo test --all-targets "${features[@]}" # ---------------------------------------------------------------------------- #