From 785c53832a5369743d8dac470078f5deeefe782c Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Fri, 14 Nov 2025 22:30:07 +0000 Subject: [PATCH 1/5] Extend .gitignore Signed-off-by: Bo Chen --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ad67955..690480a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ target # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +Cargo.lock +.vscode From 7b6073c99dbbe1527a3e1fc09a145fc462f5b6cc Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Wed, 13 Aug 2025 22:46:27 +0000 Subject: [PATCH 2/5] Add a new crate 'iommufd-bindings' Signed-off-by: Bo Chen --- Cargo.toml | 3 + iommufd-bindings/Cargo.toml | 10 + iommufd-bindings/LICENSE | 201 ++++++++++++++ iommufd-bindings/README.md | 53 ++++ iommufd-bindings/src/iommufd.rs | 451 ++++++++++++++++++++++++++++++++ iommufd-bindings/src/lib.rs | 9 + 6 files changed, 727 insertions(+) create mode 100644 Cargo.toml create mode 100644 iommufd-bindings/Cargo.toml create mode 100644 iommufd-bindings/LICENSE create mode 100644 iommufd-bindings/README.md create mode 100644 iommufd-bindings/src/iommufd.rs create mode 100644 iommufd-bindings/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ae44ca5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["iommufd-bindings"] +resolver = "2" diff --git a/iommufd-bindings/Cargo.toml b/iommufd-bindings/Cargo.toml new file mode 100644 index 0000000..f2db44a --- /dev/null +++ b/iommufd-bindings/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "iommufd-bindings" +version = "0.1.0" +authors = ["The Cloud Hypervisor Authors"] +license = "Apache-2.0" +description = "Rust FFI bindings to iommufd generated using bindgen." +repository = "https://github.com/cloud-hypervisor/iommufd" +readme = "README.md" +edition = "2024" +keywords = ["iommufd"] diff --git a/iommufd-bindings/LICENSE b/iommufd-bindings/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/iommufd-bindings/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/iommufd-bindings/README.md b/iommufd-bindings/README.md new file mode 100644 index 0000000..97f04d7 --- /dev/null +++ b/iommufd-bindings/README.md @@ -0,0 +1,53 @@ +# iommufd-bindings +Rust FFI bindings to iommufd uAPIs, generated using +[bindgen](https://crates.io/crates/bindgen). The bindings exported by +this crate are statically generated using header files associated with +a specific kernel version, and are not automatically synced with the +kernel version running on a particular host. The user must ensure that +specific structures, members, or constants are supported and valid for the +kernel version they are using. + +Currently, the bindings are generated using bindgen version 0.72.0 and +kernel version [v6.6](https://github.com/torvalds/linux/tree/v6.6). + +## Regenerating Bindings + +### Bindgen +Install bindgen version 0.72.0 +```bash +cargo install bindgen-cli --vers 0.72.0 +``` + +### Linux Kernel +Generating bindings depends on the Linux kernel, so you need to have the +repository on your machine. + +```bash +git clone https://github.com/torvalds/linux.git +``` + +### Example for regenerating + +For this example we assume that you have both linux and iommufd-bindings +repositories in your root and we use linux version v6.6 as example. + +```bash +# linux is the repository that you cloned previously. +cd linux + +# Step 1: Checkout the version you want to generate the bindings for. +git checkout tags/v6.6 + +# Step 2: Generate the bindings from the kernel headers. +make headers_install INSTALL_HDR_PATH=iommufd_headers +cd iommufd_headers +bindgen include/linux/iommufd.h -o iommufd.rs \ + --impl-debug --with-derive-default \ + --with-derive-partialeq --impl-partialeq \ + -- -Iinclude + +cd ~ + +# Step 3: Copy the generated files to the new version module. +cp linux/iommufd_headers/iommufd.rs iommufd-bindings/src/iommufd.rs +``` \ No newline at end of file diff --git a/iommufd-bindings/src/iommufd.rs b/iommufd-bindings/src/iommufd.rs new file mode 100644 index 0000000..9c99fb3 --- /dev/null +++ b/iommufd-bindings/src/iommufd.rs @@ -0,0 +1,451 @@ +/* automatically generated by rust-bindgen 0.72.0 */ + +pub const __BITS_PER_LONG: u32 = 64; +pub const __FD_SETSIZE: u32 = 1024; +pub const _IOC_NRBITS: u32 = 8; +pub const _IOC_TYPEBITS: u32 = 8; +pub const _IOC_SIZEBITS: u32 = 14; +pub const _IOC_DIRBITS: u32 = 2; +pub const _IOC_NRMASK: u32 = 255; +pub const _IOC_TYPEMASK: u32 = 255; +pub const _IOC_SIZEMASK: u32 = 16383; +pub const _IOC_DIRMASK: u32 = 3; +pub const _IOC_NRSHIFT: u32 = 0; +pub const _IOC_TYPESHIFT: u32 = 8; +pub const _IOC_SIZESHIFT: u32 = 16; +pub const _IOC_DIRSHIFT: u32 = 30; +pub const _IOC_NONE: u32 = 0; +pub const _IOC_WRITE: u32 = 1; +pub const _IOC_READ: u32 = 2; +pub const IOC_IN: u32 = 1073741824; +pub const IOC_OUT: u32 = 2147483648; +pub const IOC_INOUT: u32 = 3221225472; +pub const IOCSIZE_MASK: u32 = 1073676288; +pub const IOCSIZE_SHIFT: u32 = 16; +pub const IOMMUFD_TYPE: u8 = 59u8; +pub type __s8 = ::std::os::raw::c_schar; +pub type __u8 = ::std::os::raw::c_uchar; +pub type __s16 = ::std::os::raw::c_short; +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 __s64 = ::std::os::raw::c_longlong; +pub type __u64 = ::std::os::raw::c_ulonglong; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct __kernel_fd_set { + pub fds_bits: [::std::os::raw::c_ulong; 16usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of __kernel_fd_set"][::std::mem::size_of::<__kernel_fd_set>() - 128usize]; + ["Alignment of __kernel_fd_set"][::std::mem::align_of::<__kernel_fd_set>() - 8usize]; + ["Offset of field: __kernel_fd_set::fds_bits"] + [::std::mem::offset_of!(__kernel_fd_set, fds_bits) - 0usize]; +}; +pub type __kernel_sighandler_t = + ::std::option::Option; +pub type __kernel_key_t = ::std::os::raw::c_int; +pub type __kernel_mqd_t = ::std::os::raw::c_int; +pub type __kernel_old_uid_t = ::std::os::raw::c_ushort; +pub type __kernel_old_gid_t = ::std::os::raw::c_ushort; +pub type __kernel_old_dev_t = ::std::os::raw::c_ulong; +pub type __kernel_long_t = ::std::os::raw::c_long; +pub type __kernel_ulong_t = ::std::os::raw::c_ulong; +pub type __kernel_ino_t = __kernel_ulong_t; +pub type __kernel_mode_t = ::std::os::raw::c_uint; +pub type __kernel_pid_t = ::std::os::raw::c_int; +pub type __kernel_ipc_pid_t = ::std::os::raw::c_int; +pub type __kernel_uid_t = ::std::os::raw::c_uint; +pub type __kernel_gid_t = ::std::os::raw::c_uint; +pub type __kernel_suseconds_t = __kernel_long_t; +pub type __kernel_daddr_t = ::std::os::raw::c_int; +pub type __kernel_uid32_t = ::std::os::raw::c_uint; +pub type __kernel_gid32_t = ::std::os::raw::c_uint; +pub type __kernel_size_t = __kernel_ulong_t; +pub type __kernel_ssize_t = __kernel_long_t; +pub type __kernel_ptrdiff_t = __kernel_long_t; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct __kernel_fsid_t { + pub val: [::std::os::raw::c_int; 2usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of __kernel_fsid_t"][::std::mem::size_of::<__kernel_fsid_t>() - 8usize]; + ["Alignment of __kernel_fsid_t"][::std::mem::align_of::<__kernel_fsid_t>() - 4usize]; + ["Offset of field: __kernel_fsid_t::val"] + [::std::mem::offset_of!(__kernel_fsid_t, val) - 0usize]; +}; +pub type __kernel_off_t = __kernel_long_t; +pub type __kernel_loff_t = ::std::os::raw::c_longlong; +pub type __kernel_old_time_t = __kernel_long_t; +pub type __kernel_time_t = __kernel_long_t; +pub type __kernel_time64_t = ::std::os::raw::c_longlong; +pub type __kernel_clock_t = __kernel_long_t; +pub type __kernel_timer_t = ::std::os::raw::c_int; +pub type __kernel_clockid_t = ::std::os::raw::c_int; +pub type __kernel_caddr_t = *mut ::std::os::raw::c_char; +pub type __kernel_uid16_t = ::std::os::raw::c_ushort; +pub type __kernel_gid16_t = ::std::os::raw::c_ushort; +pub type __s128 = i128; +pub type __u128 = u128; +pub type __le16 = __u16; +pub type __be16 = __u16; +pub type __le32 = __u32; +pub type __be32 = __u32; +pub type __le64 = __u64; +pub type __be64 = __u64; +pub type __sum16 = __u16; +pub type __wsum = __u32; +pub type __poll_t = ::std::os::raw::c_uint; +pub const IOMMUFD_CMD_BASE: _bindgen_ty_1 = 128; +pub const IOMMUFD_CMD_DESTROY: _bindgen_ty_1 = 128; +pub const IOMMUFD_CMD_IOAS_ALLOC: _bindgen_ty_1 = 129; +pub const IOMMUFD_CMD_IOAS_ALLOW_IOVAS: _bindgen_ty_1 = 130; +pub const IOMMUFD_CMD_IOAS_COPY: _bindgen_ty_1 = 131; +pub const IOMMUFD_CMD_IOAS_IOVA_RANGES: _bindgen_ty_1 = 132; +pub const IOMMUFD_CMD_IOAS_MAP: _bindgen_ty_1 = 133; +pub const IOMMUFD_CMD_IOAS_UNMAP: _bindgen_ty_1 = 134; +pub const IOMMUFD_CMD_OPTION: _bindgen_ty_1 = 135; +pub const IOMMUFD_CMD_VFIO_IOAS: _bindgen_ty_1 = 136; +pub const IOMMUFD_CMD_HWPT_ALLOC: _bindgen_ty_1 = 137; +pub const IOMMUFD_CMD_GET_HW_INFO: _bindgen_ty_1 = 138; +#[doc = " DOC: General ioctl format\n\n The ioctl interface follows a general format to allow for extensibility. Each\n ioctl is passed in a structure pointer as the argument providing the size of\n the structure in the first u32. The kernel checks that any structure space\n beyond what it understands is 0. This allows userspace to use the backward\n compatible portion while consistently using the newer, larger, structures.\n\n ioctls use a standard meaning for common errnos:\n\n - ENOTTY: The IOCTL number itself is not supported at all\n - E2BIG: The IOCTL number is supported, but the provided structure has\n non-zero in a part the kernel does not understand.\n - EOPNOTSUPP: The IOCTL number is supported, and the structure is\n understood, however a known field has a value the kernel does not\n understand or support.\n - EINVAL: Everything about the IOCTL was understood, but a field is not\n correct.\n - ENOENT: An ID or IOVA provided does not exist.\n - ENOMEM: Out of memory.\n - EOVERFLOW: Mathematics overflowed.\n\n As well as additional errnos, within specific ioctls."] +pub type _bindgen_ty_1 = ::std::os::raw::c_uint; +#[doc = " struct iommu_destroy - ioctl(IOMMU_DESTROY)\n @size: sizeof(struct iommu_destroy)\n @id: iommufd object ID to destroy. Can be any destroyable object type.\n\n Destroy any object held within iommufd."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_destroy { + pub size: __u32, + pub id: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_destroy"][::std::mem::size_of::() - 8usize]; + ["Alignment of iommu_destroy"][::std::mem::align_of::() - 4usize]; + ["Offset of field: iommu_destroy::size"][::std::mem::offset_of!(iommu_destroy, size) - 0usize]; + ["Offset of field: iommu_destroy::id"][::std::mem::offset_of!(iommu_destroy, id) - 4usize]; +}; +#[doc = " struct iommu_ioas_alloc - ioctl(IOMMU_IOAS_ALLOC)\n @size: sizeof(struct iommu_ioas_alloc)\n @flags: Must be 0\n @out_ioas_id: Output IOAS ID for the allocated object\n\n Allocate an IO Address Space (IOAS) which holds an IO Virtual Address (IOVA)\n to memory mapping."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_ioas_alloc { + pub size: __u32, + pub flags: __u32, + pub out_ioas_id: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_ioas_alloc"][::std::mem::size_of::() - 12usize]; + ["Alignment of iommu_ioas_alloc"][::std::mem::align_of::() - 4usize]; + ["Offset of field: iommu_ioas_alloc::size"] + [::std::mem::offset_of!(iommu_ioas_alloc, size) - 0usize]; + ["Offset of field: iommu_ioas_alloc::flags"] + [::std::mem::offset_of!(iommu_ioas_alloc, flags) - 4usize]; + ["Offset of field: iommu_ioas_alloc::out_ioas_id"] + [::std::mem::offset_of!(iommu_ioas_alloc, out_ioas_id) - 8usize]; +}; +#[doc = " struct iommu_iova_range - ioctl(IOMMU_IOVA_RANGE)\n @start: First IOVA\n @last: Inclusive last IOVA\n\n An interval in IOVA space."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_iova_range { + pub start: __u64, + pub last: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_iova_range"][::std::mem::size_of::() - 16usize]; + ["Alignment of iommu_iova_range"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_iova_range::start"] + [::std::mem::offset_of!(iommu_iova_range, start) - 0usize]; + ["Offset of field: iommu_iova_range::last"] + [::std::mem::offset_of!(iommu_iova_range, last) - 8usize]; +}; +#[doc = " struct iommu_ioas_iova_ranges - ioctl(IOMMU_IOAS_IOVA_RANGES)\n @size: sizeof(struct iommu_ioas_iova_ranges)\n @ioas_id: IOAS ID to read ranges from\n @num_iovas: Input/Output total number of ranges in the IOAS\n @__reserved: Must be 0\n @allowed_iovas: Pointer to the output array of struct iommu_iova_range\n @out_iova_alignment: Minimum alignment required for mapping IOVA\n\n Query an IOAS for ranges of allowed IOVAs. Mapping IOVA outside these ranges\n is not allowed. num_iovas will be set to the total number of iovas and\n the allowed_iovas[] will be filled in as space permits.\n\n The allowed ranges are dependent on the HW path the DMA operation takes, and\n can change during the lifetime of the IOAS. A fresh empty IOAS will have a\n full range, and each attached device will narrow the ranges based on that\n device's HW restrictions. Detaching a device can widen the ranges. Userspace\n should query ranges after every attach/detach to know what IOVAs are valid\n for mapping.\n\n On input num_iovas is the length of the allowed_iovas array. On output it is\n the total number of iovas filled in. The ioctl will return -EMSGSIZE and set\n num_iovas to the required value if num_iovas is too small. In this case the\n caller should allocate a larger output array and re-issue the ioctl.\n\n out_iova_alignment returns the minimum IOVA alignment that can be given\n to IOMMU_IOAS_MAP/COPY. IOVA's must satisfy::\n\n starting_iova % out_iova_alignment == 0\n (starting_iova + length) % out_iova_alignment == 0\n\n out_iova_alignment can be 1 indicating any IOVA is allowed. It cannot\n be higher than the system PAGE_SIZE."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_ioas_iova_ranges { + pub size: __u32, + pub ioas_id: __u32, + pub num_iovas: __u32, + pub __reserved: __u32, + pub allowed_iovas: __u64, + pub out_iova_alignment: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_ioas_iova_ranges"][::std::mem::size_of::() - 32usize]; + ["Alignment of iommu_ioas_iova_ranges"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_ioas_iova_ranges::size"] + [::std::mem::offset_of!(iommu_ioas_iova_ranges, size) - 0usize]; + ["Offset of field: iommu_ioas_iova_ranges::ioas_id"] + [::std::mem::offset_of!(iommu_ioas_iova_ranges, ioas_id) - 4usize]; + ["Offset of field: iommu_ioas_iova_ranges::num_iovas"] + [::std::mem::offset_of!(iommu_ioas_iova_ranges, num_iovas) - 8usize]; + ["Offset of field: iommu_ioas_iova_ranges::__reserved"] + [::std::mem::offset_of!(iommu_ioas_iova_ranges, __reserved) - 12usize]; + ["Offset of field: iommu_ioas_iova_ranges::allowed_iovas"] + [::std::mem::offset_of!(iommu_ioas_iova_ranges, allowed_iovas) - 16usize]; + ["Offset of field: iommu_ioas_iova_ranges::out_iova_alignment"] + [::std::mem::offset_of!(iommu_ioas_iova_ranges, out_iova_alignment) - 24usize]; +}; +#[doc = " struct iommu_ioas_allow_iovas - ioctl(IOMMU_IOAS_ALLOW_IOVAS)\n @size: sizeof(struct iommu_ioas_allow_iovas)\n @ioas_id: IOAS ID to allow IOVAs from\n @num_iovas: Input/Output total number of ranges in the IOAS\n @__reserved: Must be 0\n @allowed_iovas: Pointer to array of struct iommu_iova_range\n\n Ensure a range of IOVAs are always available for allocation. If this call\n succeeds then IOMMU_IOAS_IOVA_RANGES will never return a list of IOVA ranges\n that are narrower than the ranges provided here. This call will fail if\n IOMMU_IOAS_IOVA_RANGES is currently narrower than the given ranges.\n\n When an IOAS is first created the IOVA_RANGES will be maximally sized, and as\n devices are attached the IOVA will narrow based on the device restrictions.\n When an allowed range is specified any narrowing will be refused, ie device\n attachment can fail if the device requires limiting within the allowed range.\n\n Automatic IOVA allocation is also impacted by this call. MAP will only\n allocate within the allowed IOVAs if they are present.\n\n This call replaces the entire allowed list with the given list."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_ioas_allow_iovas { + pub size: __u32, + pub ioas_id: __u32, + pub num_iovas: __u32, + pub __reserved: __u32, + pub allowed_iovas: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_ioas_allow_iovas"][::std::mem::size_of::() - 24usize]; + ["Alignment of iommu_ioas_allow_iovas"] + [::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_ioas_allow_iovas::size"] + [::std::mem::offset_of!(iommu_ioas_allow_iovas, size) - 0usize]; + ["Offset of field: iommu_ioas_allow_iovas::ioas_id"] + [::std::mem::offset_of!(iommu_ioas_allow_iovas, ioas_id) - 4usize]; + ["Offset of field: iommu_ioas_allow_iovas::num_iovas"] + [::std::mem::offset_of!(iommu_ioas_allow_iovas, num_iovas) - 8usize]; + ["Offset of field: iommu_ioas_allow_iovas::__reserved"] + [::std::mem::offset_of!(iommu_ioas_allow_iovas, __reserved) - 12usize]; + ["Offset of field: iommu_ioas_allow_iovas::allowed_iovas"] + [::std::mem::offset_of!(iommu_ioas_allow_iovas, allowed_iovas) - 16usize]; +}; +pub const iommufd_ioas_map_flags_IOMMU_IOAS_MAP_FIXED_IOVA: iommufd_ioas_map_flags = 1; +pub const iommufd_ioas_map_flags_IOMMU_IOAS_MAP_WRITEABLE: iommufd_ioas_map_flags = 2; +pub const iommufd_ioas_map_flags_IOMMU_IOAS_MAP_READABLE: iommufd_ioas_map_flags = 4; +#[doc = " enum iommufd_ioas_map_flags - Flags for map and copy\n @IOMMU_IOAS_MAP_FIXED_IOVA: If clear the kernel will compute an appropriate\n IOVA to place the mapping at\n @IOMMU_IOAS_MAP_WRITEABLE: DMA is allowed to write to this mapping\n @IOMMU_IOAS_MAP_READABLE: DMA is allowed to read from this mapping"] +pub type iommufd_ioas_map_flags = ::std::os::raw::c_uint; +#[doc = " struct iommu_ioas_map - ioctl(IOMMU_IOAS_MAP)\n @size: sizeof(struct iommu_ioas_map)\n @flags: Combination of enum iommufd_ioas_map_flags\n @ioas_id: IOAS ID to change the mapping of\n @__reserved: Must be 0\n @user_va: Userspace pointer to start mapping from\n @length: Number of bytes to map\n @iova: IOVA the mapping was placed at. If IOMMU_IOAS_MAP_FIXED_IOVA is set\n then this must be provided as input.\n\n Set an IOVA mapping from a user pointer. If FIXED_IOVA is specified then the\n mapping will be established at iova, otherwise a suitable location based on\n the reserved and allowed lists will be automatically selected and returned in\n iova.\n\n If IOMMU_IOAS_MAP_FIXED_IOVA is specified then the iova range must currently\n be unused, existing IOVA cannot be replaced."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_ioas_map { + pub size: __u32, + pub flags: __u32, + pub ioas_id: __u32, + pub __reserved: __u32, + pub user_va: __u64, + pub length: __u64, + pub iova: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_ioas_map"][::std::mem::size_of::() - 40usize]; + ["Alignment of iommu_ioas_map"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_ioas_map::size"] + [::std::mem::offset_of!(iommu_ioas_map, size) - 0usize]; + ["Offset of field: iommu_ioas_map::flags"] + [::std::mem::offset_of!(iommu_ioas_map, flags) - 4usize]; + ["Offset of field: iommu_ioas_map::ioas_id"] + [::std::mem::offset_of!(iommu_ioas_map, ioas_id) - 8usize]; + ["Offset of field: iommu_ioas_map::__reserved"] + [::std::mem::offset_of!(iommu_ioas_map, __reserved) - 12usize]; + ["Offset of field: iommu_ioas_map::user_va"] + [::std::mem::offset_of!(iommu_ioas_map, user_va) - 16usize]; + ["Offset of field: iommu_ioas_map::length"] + [::std::mem::offset_of!(iommu_ioas_map, length) - 24usize]; + ["Offset of field: iommu_ioas_map::iova"] + [::std::mem::offset_of!(iommu_ioas_map, iova) - 32usize]; +}; +#[doc = " struct iommu_ioas_copy - ioctl(IOMMU_IOAS_COPY)\n @size: sizeof(struct iommu_ioas_copy)\n @flags: Combination of enum iommufd_ioas_map_flags\n @dst_ioas_id: IOAS ID to change the mapping of\n @src_ioas_id: IOAS ID to copy from\n @length: Number of bytes to copy and map\n @dst_iova: IOVA the mapping was placed at. If IOMMU_IOAS_MAP_FIXED_IOVA is\n set then this must be provided as input.\n @src_iova: IOVA to start the copy\n\n Copy an already existing mapping from src_ioas_id and establish it in\n dst_ioas_id. The src iova/length must exactly match a range used with\n IOMMU_IOAS_MAP.\n\n This may be used to efficiently clone a subset of an IOAS to another, or as a\n kind of 'cache' to speed up mapping. Copy has an efficiency advantage over\n establishing equivalent new mappings, as internal resources are shared, and\n the kernel will pin the user memory only once."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_ioas_copy { + pub size: __u32, + pub flags: __u32, + pub dst_ioas_id: __u32, + pub src_ioas_id: __u32, + pub length: __u64, + pub dst_iova: __u64, + pub src_iova: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_ioas_copy"][::std::mem::size_of::() - 40usize]; + ["Alignment of iommu_ioas_copy"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_ioas_copy::size"] + [::std::mem::offset_of!(iommu_ioas_copy, size) - 0usize]; + ["Offset of field: iommu_ioas_copy::flags"] + [::std::mem::offset_of!(iommu_ioas_copy, flags) - 4usize]; + ["Offset of field: iommu_ioas_copy::dst_ioas_id"] + [::std::mem::offset_of!(iommu_ioas_copy, dst_ioas_id) - 8usize]; + ["Offset of field: iommu_ioas_copy::src_ioas_id"] + [::std::mem::offset_of!(iommu_ioas_copy, src_ioas_id) - 12usize]; + ["Offset of field: iommu_ioas_copy::length"] + [::std::mem::offset_of!(iommu_ioas_copy, length) - 16usize]; + ["Offset of field: iommu_ioas_copy::dst_iova"] + [::std::mem::offset_of!(iommu_ioas_copy, dst_iova) - 24usize]; + ["Offset of field: iommu_ioas_copy::src_iova"] + [::std::mem::offset_of!(iommu_ioas_copy, src_iova) - 32usize]; +}; +#[doc = " struct iommu_ioas_unmap - ioctl(IOMMU_IOAS_UNMAP)\n @size: sizeof(struct iommu_ioas_unmap)\n @ioas_id: IOAS ID to change the mapping of\n @iova: IOVA to start the unmapping at\n @length: Number of bytes to unmap, and return back the bytes unmapped\n\n Unmap an IOVA range. The iova/length must be a superset of a previously\n mapped range used with IOMMU_IOAS_MAP or IOMMU_IOAS_COPY. Splitting or\n truncating ranges is not allowed. The values 0 to U64_MAX will unmap\n everything."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_ioas_unmap { + pub size: __u32, + pub ioas_id: __u32, + pub iova: __u64, + pub length: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_ioas_unmap"][::std::mem::size_of::() - 24usize]; + ["Alignment of iommu_ioas_unmap"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_ioas_unmap::size"] + [::std::mem::offset_of!(iommu_ioas_unmap, size) - 0usize]; + ["Offset of field: iommu_ioas_unmap::ioas_id"] + [::std::mem::offset_of!(iommu_ioas_unmap, ioas_id) - 4usize]; + ["Offset of field: iommu_ioas_unmap::iova"] + [::std::mem::offset_of!(iommu_ioas_unmap, iova) - 8usize]; + ["Offset of field: iommu_ioas_unmap::length"] + [::std::mem::offset_of!(iommu_ioas_unmap, length) - 16usize]; +}; +pub const iommufd_option_IOMMU_OPTION_RLIMIT_MODE: iommufd_option = 0; +pub const iommufd_option_IOMMU_OPTION_HUGE_PAGES: iommufd_option = 1; +#[doc = " enum iommufd_option - ioctl(IOMMU_OPTION_RLIMIT_MODE) and\n ioctl(IOMMU_OPTION_HUGE_PAGES)\n @IOMMU_OPTION_RLIMIT_MODE:\n Change how RLIMIT_MEMLOCK accounting works. The caller must have privilege\n to invoke this. Value 0 (default) is user based accouting, 1 uses process\n based accounting. Global option, object_id must be 0\n @IOMMU_OPTION_HUGE_PAGES:\n Value 1 (default) allows contiguous pages to be combined when generating\n iommu mappings. Value 0 disables combining, everything is mapped to\n PAGE_SIZE. This can be useful for benchmarking. This is a per-IOAS\n option, the object_id must be the IOAS ID."] +pub type iommufd_option = ::std::os::raw::c_uint; +pub const iommufd_option_ops_IOMMU_OPTION_OP_SET: iommufd_option_ops = 0; +pub const iommufd_option_ops_IOMMU_OPTION_OP_GET: iommufd_option_ops = 1; +#[doc = " enum iommufd_option_ops - ioctl(IOMMU_OPTION_OP_SET) and\n ioctl(IOMMU_OPTION_OP_GET)\n @IOMMU_OPTION_OP_SET: Set the option's value\n @IOMMU_OPTION_OP_GET: Get the option's value"] +pub type iommufd_option_ops = ::std::os::raw::c_uint; +#[doc = " struct iommu_option - iommu option multiplexer\n @size: sizeof(struct iommu_option)\n @option_id: One of enum iommufd_option\n @op: One of enum iommufd_option_ops\n @__reserved: Must be 0\n @object_id: ID of the object if required\n @val64: Option value to set or value returned on get\n\n Change a simple option value. This multiplexor allows controlling options\n on objects. IOMMU_OPTION_OP_SET will load an option and IOMMU_OPTION_OP_GET\n will return the current value."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_option { + pub size: __u32, + pub option_id: __u32, + pub op: __u16, + pub __reserved: __u16, + pub object_id: __u32, + pub val64: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_option"][::std::mem::size_of::() - 24usize]; + ["Alignment of iommu_option"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_option::size"][::std::mem::offset_of!(iommu_option, size) - 0usize]; + ["Offset of field: iommu_option::option_id"] + [::std::mem::offset_of!(iommu_option, option_id) - 4usize]; + ["Offset of field: iommu_option::op"][::std::mem::offset_of!(iommu_option, op) - 8usize]; + ["Offset of field: iommu_option::__reserved"] + [::std::mem::offset_of!(iommu_option, __reserved) - 10usize]; + ["Offset of field: iommu_option::object_id"] + [::std::mem::offset_of!(iommu_option, object_id) - 12usize]; + ["Offset of field: iommu_option::val64"][::std::mem::offset_of!(iommu_option, val64) - 16usize]; +}; +pub const iommufd_vfio_ioas_op_IOMMU_VFIO_IOAS_GET: iommufd_vfio_ioas_op = 0; +pub const iommufd_vfio_ioas_op_IOMMU_VFIO_IOAS_SET: iommufd_vfio_ioas_op = 1; +pub const iommufd_vfio_ioas_op_IOMMU_VFIO_IOAS_CLEAR: iommufd_vfio_ioas_op = 2; +#[doc = " enum iommufd_vfio_ioas_op - IOMMU_VFIO_IOAS_* ioctls\n @IOMMU_VFIO_IOAS_GET: Get the current compatibility IOAS\n @IOMMU_VFIO_IOAS_SET: Change the current compatibility IOAS\n @IOMMU_VFIO_IOAS_CLEAR: Disable VFIO compatibility"] +pub type iommufd_vfio_ioas_op = ::std::os::raw::c_uint; +#[doc = " struct iommu_vfio_ioas - ioctl(IOMMU_VFIO_IOAS)\n @size: sizeof(struct iommu_vfio_ioas)\n @ioas_id: For IOMMU_VFIO_IOAS_SET the input IOAS ID to set\n For IOMMU_VFIO_IOAS_GET will output the IOAS ID\n @op: One of enum iommufd_vfio_ioas_op\n @__reserved: Must be 0\n\n The VFIO compatibility support uses a single ioas because VFIO APIs do not\n support the ID field. Set or Get the IOAS that VFIO compatibility will use.\n When VFIO_GROUP_SET_CONTAINER is used on an iommufd it will get the\n compatibility ioas, either by taking what is already set, or auto creating\n one. From then on VFIO will continue to use that ioas and is not effected by\n this ioctl. SET or CLEAR does not destroy any auto-created IOAS."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_vfio_ioas { + pub size: __u32, + pub ioas_id: __u32, + pub op: __u16, + pub __reserved: __u16, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_vfio_ioas"][::std::mem::size_of::() - 12usize]; + ["Alignment of iommu_vfio_ioas"][::std::mem::align_of::() - 4usize]; + ["Offset of field: iommu_vfio_ioas::size"] + [::std::mem::offset_of!(iommu_vfio_ioas, size) - 0usize]; + ["Offset of field: iommu_vfio_ioas::ioas_id"] + [::std::mem::offset_of!(iommu_vfio_ioas, ioas_id) - 4usize]; + ["Offset of field: iommu_vfio_ioas::op"][::std::mem::offset_of!(iommu_vfio_ioas, op) - 8usize]; + ["Offset of field: iommu_vfio_ioas::__reserved"] + [::std::mem::offset_of!(iommu_vfio_ioas, __reserved) - 10usize]; +}; +#[doc = " struct iommu_hwpt_alloc - ioctl(IOMMU_HWPT_ALLOC)\n @size: sizeof(struct iommu_hwpt_alloc)\n @flags: Must be 0\n @dev_id: The device to allocate this HWPT for\n @pt_id: The IOAS to connect this HWPT to\n @out_hwpt_id: The ID of the new HWPT\n @__reserved: Must be 0\n\n Explicitly allocate a hardware page table object. This is the same object\n type that is returned by iommufd_device_attach() and represents the\n underlying iommu driver's iommu_domain kernel object.\n\n A HWPT will be created with the IOVA mappings from the given IOAS."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_hwpt_alloc { + pub size: __u32, + pub flags: __u32, + pub dev_id: __u32, + pub pt_id: __u32, + pub out_hwpt_id: __u32, + pub __reserved: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_hwpt_alloc"][::std::mem::size_of::() - 24usize]; + ["Alignment of iommu_hwpt_alloc"][::std::mem::align_of::() - 4usize]; + ["Offset of field: iommu_hwpt_alloc::size"] + [::std::mem::offset_of!(iommu_hwpt_alloc, size) - 0usize]; + ["Offset of field: iommu_hwpt_alloc::flags"] + [::std::mem::offset_of!(iommu_hwpt_alloc, flags) - 4usize]; + ["Offset of field: iommu_hwpt_alloc::dev_id"] + [::std::mem::offset_of!(iommu_hwpt_alloc, dev_id) - 8usize]; + ["Offset of field: iommu_hwpt_alloc::pt_id"] + [::std::mem::offset_of!(iommu_hwpt_alloc, pt_id) - 12usize]; + ["Offset of field: iommu_hwpt_alloc::out_hwpt_id"] + [::std::mem::offset_of!(iommu_hwpt_alloc, out_hwpt_id) - 16usize]; + ["Offset of field: iommu_hwpt_alloc::__reserved"] + [::std::mem::offset_of!(iommu_hwpt_alloc, __reserved) - 20usize]; +}; +#[doc = " struct iommu_hw_info_vtd - Intel VT-d hardware information\n\n @flags: Must be 0\n @__reserved: Must be 0\n\n @cap_reg: Value of Intel VT-d capability register defined in VT-d spec\n section 11.4.2 Capability Register.\n @ecap_reg: Value of Intel VT-d capability register defined in VT-d spec\n section 11.4.3 Extended Capability Register.\n\n User needs to understand the Intel VT-d specification to decode the\n register value."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_hw_info_vtd { + pub flags: __u32, + pub __reserved: __u32, + pub cap_reg: __u64, + pub ecap_reg: __u64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_hw_info_vtd"][::std::mem::size_of::() - 24usize]; + ["Alignment of iommu_hw_info_vtd"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_hw_info_vtd::flags"] + [::std::mem::offset_of!(iommu_hw_info_vtd, flags) - 0usize]; + ["Offset of field: iommu_hw_info_vtd::__reserved"] + [::std::mem::offset_of!(iommu_hw_info_vtd, __reserved) - 4usize]; + ["Offset of field: iommu_hw_info_vtd::cap_reg"] + [::std::mem::offset_of!(iommu_hw_info_vtd, cap_reg) - 8usize]; + ["Offset of field: iommu_hw_info_vtd::ecap_reg"] + [::std::mem::offset_of!(iommu_hw_info_vtd, ecap_reg) - 16usize]; +}; +pub const iommu_hw_info_type_IOMMU_HW_INFO_TYPE_NONE: iommu_hw_info_type = 0; +pub const iommu_hw_info_type_IOMMU_HW_INFO_TYPE_INTEL_VTD: iommu_hw_info_type = 1; +#[doc = " enum iommu_hw_info_type - IOMMU Hardware Info Types\n @IOMMU_HW_INFO_TYPE_NONE: Used by the drivers that do not report hardware\n info\n @IOMMU_HW_INFO_TYPE_INTEL_VTD: Intel VT-d iommu info type"] +pub type iommu_hw_info_type = ::std::os::raw::c_uint; +#[doc = " struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO)\n @size: sizeof(struct iommu_hw_info)\n @flags: Must be 0\n @dev_id: The device bound to the iommufd\n @data_len: Input the length of a user buffer in bytes. Output the length of\n data that kernel supports\n @data_uptr: User pointer to a user-space buffer used by the kernel to fill\n the iommu type specific hardware information data\n @out_data_type: Output the iommu hardware info type as defined in the enum\n iommu_hw_info_type.\n @__reserved: Must be 0\n\n Query an iommu type specific hardware information data from an iommu behind\n a given device that has been bound to iommufd. This hardware info data will\n be used to sync capabilities between the virtual iommu and the physical\n iommu, e.g. a nested translation setup needs to check the hardware info, so\n a guest stage-1 page table can be compatible with the physical iommu.\n\n To capture an iommu type specific hardware information data, @data_uptr and\n its length @data_len must be provided. Trailing bytes will be zeroed if the\n user buffer is larger than the data that kernel has. Otherwise, kernel only\n fills the buffer using the given length in @data_len. If the ioctl succeeds,\n @data_len will be updated to the length that kernel actually supports,\n @out_data_type will be filled to decode the data filled in the buffer\n pointed by @data_uptr. Input @data_len == zero is allowed."] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub struct iommu_hw_info { + pub size: __u32, + pub flags: __u32, + pub dev_id: __u32, + pub data_len: __u32, + pub data_uptr: __u64, + pub out_data_type: __u32, + pub __reserved: __u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of iommu_hw_info"][::std::mem::size_of::() - 32usize]; + ["Alignment of iommu_hw_info"][::std::mem::align_of::() - 8usize]; + ["Offset of field: iommu_hw_info::size"][::std::mem::offset_of!(iommu_hw_info, size) - 0usize]; + ["Offset of field: iommu_hw_info::flags"] + [::std::mem::offset_of!(iommu_hw_info, flags) - 4usize]; + ["Offset of field: iommu_hw_info::dev_id"] + [::std::mem::offset_of!(iommu_hw_info, dev_id) - 8usize]; + ["Offset of field: iommu_hw_info::data_len"] + [::std::mem::offset_of!(iommu_hw_info, data_len) - 12usize]; + ["Offset of field: iommu_hw_info::data_uptr"] + [::std::mem::offset_of!(iommu_hw_info, data_uptr) - 16usize]; + ["Offset of field: iommu_hw_info::out_data_type"] + [::std::mem::offset_of!(iommu_hw_info, out_data_type) - 24usize]; + ["Offset of field: iommu_hw_info::__reserved"] + [::std::mem::offset_of!(iommu_hw_info, __reserved) - 28usize]; +}; diff --git a/iommufd-bindings/src/lib.rs b/iommufd-bindings/src/lib.rs new file mode 100644 index 0000000..d2b913f --- /dev/null +++ b/iommufd-bindings/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright © 2025 Crusoe Energy Systems LLC +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[allow(non_camel_case_types, non_upper_case_globals)] +pub mod iommufd; + +pub use iommufd::*; From 176d1368694819b785f086c4dd7cc86063b43c16 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Wed, 13 Aug 2025 23:43:04 +0000 Subject: [PATCH 3/5] Add a new crate 'iommufd-ioctls' Signed-off-by: Bo Chen --- Cargo.toml | 2 +- iommufd-ioctls/Cargo.toml | 15 ++ iommufd-ioctls/LICENSE | 201 +++++++++++++++++++++++++++ iommufd-ioctls/README.md | 7 + iommufd-ioctls/src/iommufd_ioctls.rs | 122 ++++++++++++++++ iommufd-ioctls/src/lib.rs | 29 ++++ 6 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 iommufd-ioctls/Cargo.toml create mode 100644 iommufd-ioctls/LICENSE create mode 100644 iommufd-ioctls/README.md create mode 100644 iommufd-ioctls/src/iommufd_ioctls.rs create mode 100644 iommufd-ioctls/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index ae44ca5..cdaddb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["iommufd-bindings"] +members = ["iommufd-bindings", "iommufd-ioctls"] resolver = "2" diff --git a/iommufd-ioctls/Cargo.toml b/iommufd-ioctls/Cargo.toml new file mode 100644 index 0000000..f8d4d92 --- /dev/null +++ b/iommufd-ioctls/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "iommufd-ioctls" +version = "0.1.0" +authors = ["The Cloud Hypervisor Authors"] +license = "Apache-2.0" +description = "Safe wrappers over IOMMUFD uAPIs" +repository = "https://github.com/cloud-hypervisor/iommufd" +readme = "README.md" +edition = "2024" +keywords = ["iommufd"] + +[dependencies] +iommufd-bindings = { version = "0.1.0", path = "../iommufd-bindings" } +thiserror = "2.0.17" +vmm-sys-util = { version = "0.14.0" } diff --git a/iommufd-ioctls/LICENSE b/iommufd-ioctls/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/iommufd-ioctls/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/iommufd-ioctls/README.md b/iommufd-ioctls/README.md new file mode 100644 index 0000000..f6758f2 --- /dev/null +++ b/iommufd-ioctls/README.md @@ -0,0 +1,7 @@ +# iommufd-ioctls + +The iommufd-ioctls crate provides safe wrappers over the +[IOMMUFD uAPIs](https://docs.kernel.org/userspace-api/iommufd.html#iommufd-user-api), a set +of ioctls used to control the IOMMU subsystem as it relates to managing +IO page tables from userspace. The ioctls are accessible through +structure `IommuFd`. diff --git a/iommufd-ioctls/src/iommufd_ioctls.rs b/iommufd-ioctls/src/iommufd_ioctls.rs new file mode 100644 index 0000000..c7c35ab --- /dev/null +++ b/iommufd-ioctls/src/iommufd_ioctls.rs @@ -0,0 +1,122 @@ +// Copyright © 2025 Crusoe Energy Systems LLC +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::fs::{File, OpenOptions}; +use std::os::unix::io::{AsRawFd, RawFd}; + +use iommufd_bindings::iommufd::*; +use vmm_sys_util::errno::Error as SysError; + +use crate::{IommufdError, Result}; + +pub struct IommuFd { + iommufd: File, +} + +impl IommuFd { + pub fn new() -> Result { + let iommufd = OpenOptions::new() + .read(true) + .write(true) + .open("/dev/iommu") + .map_err(IommufdError::OpenIommufd)?; + + Ok(IommuFd { iommufd }) + } + + pub fn alloc_iommu_ioas(&self, alloc_data: &mut iommu_ioas_alloc) -> Result<()> { + iommufd_syscall::alloc_iommu_ioas(self, alloc_data) + } + + pub fn map_iommu_ioas(&self, map: &iommu_ioas_map) -> Result<()> { + iommufd_syscall::map_iommu_ioas(self, map) + } + pub fn unmap_iommu_ioas(&self, unmap: &mut iommu_ioas_unmap) -> Result<()> { + iommufd_syscall::unmap_iommu_ioas(self, unmap) + } +} + +impl AsRawFd for IommuFd { + fn as_raw_fd(&self) -> RawFd { + self.iommufd.as_raw_fd() + } +} + +ioctl_io_nr!( + IOMMU_IOAS_ALLOC, + IOMMUFD_TYPE as u32, + IOMMUFD_CMD_IOAS_ALLOC +); +ioctl_io_nr!(IOMMU_IOAS_MAP, IOMMUFD_TYPE as u32, IOMMUFD_CMD_IOAS_MAP); +ioctl_io_nr!( + IOMMU_IOAS_UNMAP, + IOMMUFD_TYPE as u32, + IOMMUFD_CMD_IOAS_UNMAP +); + +// Safety: +// - absolutely trust the underlying kernel +// - absolutely trust data returned by the underlying kernel +// - assume kernel will return error if caller passes in invalid file handle, parameter or buffer. +pub(crate) mod iommufd_syscall { + use super::*; + use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref}; + + pub(crate) fn alloc_iommu_ioas( + iommufd: &IommuFd, + alloc_data: &mut iommu_ioas_alloc, + ) -> Result<()> { + // SAFETY: + // 1. The file descriptor provided by 'iommufd' is valid and open. + // 2. The 'alloc_data' points to initialized memory with expected data structure, + // and remains valid for the duration of syscall. + // 3. The return value is checked. + let ret = unsafe { ioctl_with_mut_ref(iommufd, IOMMU_IOAS_ALLOC(), alloc_data) }; + if ret < 0 { + Err(IommufdError::IommuIoasAlloc(SysError::last())) + } else { + Ok(()) + } + } + + pub(crate) fn map_iommu_ioas(iommufd: &IommuFd, map: &iommu_ioas_map) -> Result<()> { + // SAFETY: + // 1. The file descriptor provided by 'iommufd' is valid and open. + // 2. The 'map' points to initialized memory with expected data structure, + // and remains valid for the duration of syscall. + // 3. The return value is checked. + let ret = unsafe { ioctl_with_ref(iommufd, IOMMU_IOAS_MAP(), map) }; + if ret < 0 { + Err(IommufdError::IommuIoasMap(SysError::last())) + } else { + Ok(()) + } + } + pub(crate) fn unmap_iommu_ioas(iommufd: &IommuFd, unmap: &mut iommu_ioas_unmap) -> Result<()> { + // SAFETY: + // 1. The file descriptor provided by 'iommufd' is valid and open. + // 2. The 'unmap' points to initialized memory with expected data structure, + // and remains valid for the duration of syscall. + // 3. The return value is checked. + let ret = unsafe { ioctl_with_ref(iommufd, IOMMU_IOAS_UNMAP(), unmap) }; + if ret < 0 { + Err(IommufdError::IommuIoasUnmap(SysError::last())) + } else { + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_iommufd_ioctl_code() { + assert_eq!(IOMMU_IOAS_ALLOC(), 15233); + assert_eq!(IOMMU_IOAS_MAP(), 15237); + assert_eq!(IOMMU_IOAS_UNMAP(), 15238); + } +} diff --git a/iommufd-ioctls/src/lib.rs b/iommufd-ioctls/src/lib.rs new file mode 100644 index 0000000..44475d2 --- /dev/null +++ b/iommufd-ioctls/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright © 2025 Crusoe Energy Systems LLC +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[macro_use] +extern crate vmm_sys_util; + +use std::io; +use thiserror::Error; +use vmm_sys_util::errno::Error as SysError; + +pub mod iommufd_ioctls; + +pub use iommufd_ioctls::*; + +#[derive(Debug, Error)] +pub enum IommufdError { + #[error("failed to open /dev/iommufd: {0}")] + OpenIommufd(#[source] io::Error), + #[error("failed to allocate IOAS: {0}")] + IommuIoasAlloc(#[source] SysError), + #[error("failed to map an IOVA range to the IOAS: {0}")] + IommuIoasMap(#[source] SysError), + #[error("failed to unmap an IOVA range from the IOAS: {0}")] + IommuIoasUnmap(#[source] SysError), +} + +pub type Result = std::result::Result; From 97da3c4194665e5723f73ed919b441c94747e446 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Wed, 13 Aug 2025 23:49:54 +0000 Subject: [PATCH 4/5] Add top-level README.md Signed-off-by: Bo Chen --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..31354f5 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# iommufd + +The `iommufd` workspace hosts libraries related to Rust bindings and +wrappers to the +[IOMMUFD](https://docs.kernel.org/userspace-api/iommufd.html) subsystem +from the Linux kernel. It currently consists of the following crates: + +- `iommufd-bindings` -> Rust FFI bindings to IOMMUFD generated using bindgen +- `iommufd-ioctls` -> Safe wrappers over IOMMUFD uAPIs \ No newline at end of file From 6016d4ac16200f1ae81e131c1ed9e23c9d7b5c7f Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Wed, 10 Dec 2025 01:20:06 +0000 Subject: [PATCH 5/5] build: Add Github actions This includes checks for DCO, cargo fmt/clippy, cargo.toml format, and unit tests. Signed-off-by: Bo Chen Suggested-by: Rob Bradford --- .github/workflows/dco.yaml | 19 +++++++++++++++ .github/workflows/quality.yaml | 39 +++++++++++++++++++++++++++++++ .github/workflows/taplo.yaml | 19 +++++++++++++++ .github/workflows/unit_tests.yaml | 35 +++++++++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 .github/workflows/dco.yaml create mode 100644 .github/workflows/quality.yaml create mode 100644 .github/workflows/taplo.yaml create mode 100644 .github/workflows/unit_tests.yaml diff --git a/.github/workflows/dco.yaml b/.github/workflows/dco.yaml new file mode 100644 index 0000000..926ff1f --- /dev/null +++ b/.github/workflows/dco.yaml @@ -0,0 +1,19 @@ +name: DCO +on: [pull_request] + +jobs: + check: + name: DCO Check ("Signed-Off-By") + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Set up Python 3.x + uses: actions/setup-python@v6 + with: + python-version: '3.x' + - name: Check DCO + if: ${{ github.event_name == 'pull_request' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pip3 install -U dco-check diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml new file mode 100644 index 0000000..aa46581 --- /dev/null +++ b/.github/workflows/quality.yaml @@ -0,0 +1,39 @@ +name: Quality Checks +on: [pull_request, create] + +jobs: + build: + name: Quality (clippy, rustfmt) + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.rust == 'beta' }} + strategy: + fail-fast: false + matrix: + rust: [stable, beta] + target: + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Install Rust toolchain (${{ matrix.rust }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + components: rustfmt, clippy + + - name: Formatting (rustfmt) + run: cargo fmt -- --check + + - name: Clippy + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} + command: clippy + args: --target=${{ matrix.target }} --workspace --all-targets --tests --all-features -- -D warnings -D clippy::undocumented_unsafe_blocks diff --git a/.github/workflows/taplo.yaml b/.github/workflows/taplo.yaml new file mode 100644 index 0000000..1e7171a --- /dev/null +++ b/.github/workflows/taplo.yaml @@ -0,0 +1,19 @@ +name: Cargo.toml format check (taplo) +on: + pull_request: + paths: + - '**/Cargo.toml' + +jobs: + cargo_toml_format: + name: Cargo.toml format check + runs-on: ubuntu-latest + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Setup Taplo + uses: uncenter/setup-taplo@v1.0.8 + + - name: Taplo format check + run: taplo fmt --check diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml new file mode 100644 index 0000000..c87a82f --- /dev/null +++ b/.github/workflows/unit_tests.yaml @@ -0,0 +1,35 @@ +name: Unit tests +on: [pull_request, create] + +jobs: + build: + name: Unit tests + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.rust == 'beta' }} + strategy: + fail-fast: false + matrix: + rust: [stable, beta] + target: + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Install Rust toolchain (${{ matrix.rust }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + + - name: Unit tests + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.target != 'x86_64-unknown-linux-gnu' }} + command: test + args: --target=${{ matrix.target }} --workspace --all-targets --tests --all-features \ No newline at end of file