• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 //
3 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 //
5 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
6 //
7 // SPDX-License-Identifier: BSD-3-Clause
8 
9 //! Macros and functions for working with
10 //! [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html).
11 
12 use std::os::raw::{c_int, c_uint, c_ulong, c_void};
13 use std::os::unix::io::AsRawFd;
14 
15 // The only reason
16 // [_IOC](https://elixir.bootlin.com/linux/v5.10.129/source/arch/alpha/include/uapi/asm/ioctl.h#L40)
17 // is a macro in C is because C doesn't have const functions, it is always better when possible to
18 // use a const function over a macro in Rust.
19 /// Function to calculate icotl number. Mimic of
20 /// [_IOC](https://elixir.bootlin.com/linux/v5.10.129/source/arch/alpha/include/uapi/asm/ioctl.h#L40)
21 /// ```
22 /// # use std::os::raw::c_uint;
23 /// # use vmm_sys_util::ioctl::{ioctl_expr, _IOC_NONE};
24 /// const KVMIO: c_uint = 0xAE;
25 /// ioctl_expr(_IOC_NONE, KVMIO, 0x01, 0);
26 /// ```
ioctl_expr( dir: c_uint, ty: c_uint, nr: c_uint, size: c_uint, ) -> ::std::os::raw::c_ulong27 pub const fn ioctl_expr(
28     dir: c_uint,
29     ty: c_uint,
30     nr: c_uint,
31     size: c_uint,
32 ) -> ::std::os::raw::c_ulong {
33     (dir << crate::ioctl::_IOC_DIRSHIFT
34         | ty << crate::ioctl::_IOC_TYPESHIFT
35         | nr << crate::ioctl::_IOC_NRSHIFT
36         | size << crate::ioctl::_IOC_SIZESHIFT) as ::std::os::raw::c_ulong
37 }
38 
39 /// Declare a function that returns an ioctl number.
40 ///
41 /// ```
42 /// # #[macro_use] extern crate vmm_sys_util;
43 /// # use std::os::raw::c_uint;
44 /// use vmm_sys_util::ioctl::_IOC_NONE;
45 ///
46 /// const KVMIO: c_uint = 0xAE;
47 /// ioctl_ioc_nr!(KVM_CREATE_VM, _IOC_NONE, KVMIO, 0x01, 0);
48 /// ```
49 #[macro_export]
50 macro_rules! ioctl_ioc_nr {
51     ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => {
52         #[allow(non_snake_case)]
53         #[allow(clippy::cast_lossless)]
54         pub fn $name() -> ::std::os::raw::c_ulong {
55             $crate::ioctl::ioctl_expr($dir, $ty, $nr, $size)
56         }
57     };
58     ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr, $($v:ident),+) => {
59         #[allow(non_snake_case)]
60         #[allow(clippy::cast_lossless)]
61         pub fn $name($($v: ::std::os::raw::c_uint),+) -> ::std::os::raw::c_ulong {
62             $crate::ioctl::ioctl_expr($dir, $ty, $nr, $size)
63         }
64     };
65 }
66 
67 /// Declare an ioctl that transfers no data.
68 ///
69 /// ```
70 /// # #[macro_use] extern crate vmm_sys_util;
71 /// # use std::os::raw::c_uint;
72 /// const KVMIO: c_uint = 0xAE;
73 /// ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01);
74 /// ```
75 #[macro_export]
76 macro_rules! ioctl_io_nr {
77     ($name:ident, $ty:expr, $nr:expr) => {
78         ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0);
79     };
80     ($name:ident, $ty:expr, $nr:expr, $($v:ident),+) => {
81         ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0, $($v),+);
82     };
83 }
84 
85 /// Declare an ioctl that reads data.
86 ///
87 /// ```
88 /// # #[macro_use] extern crate vmm_sys_util;
89 /// const TUNTAP: ::std::os::raw::c_uint = 0x54;
90 /// ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint);
91 /// ```
92 #[macro_export]
93 macro_rules! ioctl_ior_nr {
94     ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
95         ioctl_ioc_nr!(
96             $name,
97             $crate::ioctl::_IOC_READ,
98             $ty,
99             $nr,
100             ::std::mem::size_of::<$size>() as u32
101         );
102     };
103     ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
104         ioctl_ioc_nr!(
105             $name,
106             $crate::ioctl::_IOC_READ,
107             $ty,
108             $nr,
109             ::std::mem::size_of::<$size>() as u32,
110             $($v),+
111         );
112     };
113 }
114 
115 /// Declare an ioctl that writes data.
116 ///
117 /// ```
118 /// # #[macro_use] extern crate vmm_sys_util;
119 /// const TUNTAP: ::std::os::raw::c_uint = 0x54;
120 /// ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int);
121 /// ```
122 #[macro_export]
123 macro_rules! ioctl_iow_nr {
124     ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
125         ioctl_ioc_nr!(
126             $name,
127             $crate::ioctl::_IOC_WRITE,
128             $ty,
129             $nr,
130             ::std::mem::size_of::<$size>() as u32
131         );
132     };
133     ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
134         ioctl_ioc_nr!(
135             $name,
136             $crate::ioctl::_IOC_WRITE,
137             $ty,
138             $nr,
139             ::std::mem::size_of::<$size>() as u32,
140             $($v),+
141         );
142     };
143 }
144 
145 /// Declare an ioctl that reads and writes data.
146 ///
147 /// ```
148 /// # #[macro_use] extern crate vmm_sys_util;
149 /// const VHOST: ::std::os::raw::c_uint = 0xAF;
150 /// ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, ::std::os::raw::c_int);
151 /// ```
152 #[macro_export]
153 macro_rules! ioctl_iowr_nr {
154     ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
155         ioctl_ioc_nr!(
156             $name,
157             $crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE,
158             $ty,
159             $nr,
160             ::std::mem::size_of::<$size>() as u32
161         );
162     };
163     ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
164         ioctl_ioc_nr!(
165             $name,
166             $crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE,
167             $ty,
168             $nr,
169             ::std::mem::size_of::<$size>() as u32,
170             $($v),+
171         );
172     };
173 }
174 
175 // Define IOC_* constants in a module so that we can allow missing docs on it.
176 // There is not much value in documenting these as it is code generated from
177 // kernel definitions.
178 #[allow(missing_docs)]
179 mod ioc {
180     use std::os::raw::c_uint;
181 
182     pub const _IOC_NRBITS: c_uint = 8;
183     pub const _IOC_TYPEBITS: c_uint = 8;
184     pub const _IOC_SIZEBITS: c_uint = 14;
185     pub const _IOC_DIRBITS: c_uint = 2;
186     pub const _IOC_NRMASK: c_uint = 255;
187     pub const _IOC_TYPEMASK: c_uint = 255;
188     pub const _IOC_SIZEMASK: c_uint = 16383;
189     pub const _IOC_DIRMASK: c_uint = 3;
190     pub const _IOC_NRSHIFT: c_uint = 0;
191     pub const _IOC_TYPESHIFT: c_uint = 8;
192     pub const _IOC_SIZESHIFT: c_uint = 16;
193     pub const _IOC_DIRSHIFT: c_uint = 30;
194     pub const _IOC_NONE: c_uint = 0;
195     pub const _IOC_WRITE: c_uint = 1;
196     pub const _IOC_READ: c_uint = 2;
197     pub const IOC_IN: c_uint = 1_073_741_824;
198     pub const IOC_OUT: c_uint = 2_147_483_648;
199     pub const IOC_INOUT: c_uint = 3_221_225_472;
200     pub const IOCSIZE_MASK: c_uint = 1_073_676_288;
201     pub const IOCSIZE_SHIFT: c_uint = 16;
202 }
203 pub use self::ioc::*;
204 
205 // The type of the `req` parameter is different for the `musl` library. This will enable
206 // successful build for other non-musl libraries.
207 #[cfg(target_env = "musl")]
208 type IoctlRequest = c_int;
209 #[cfg(all(not(target_env = "musl"), not(target_os = "android")))]
210 type IoctlRequest = c_ulong;
211 #[cfg(all(not(target_env = "musl"), target_os = "android"))]
212 type IoctlRequest = c_int;
213 /// Run an [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html)
214 /// with no arguments.
215 ///
216 /// # Arguments
217 ///
218 /// * `fd`: an open file descriptor corresponding to the device on which
219 /// to call the ioctl.
220 /// * `req`: a device-dependent request code.
221 ///
222 /// # Safety
223 ///
224 /// The caller should ensure to pass a valid file descriptor and have the
225 /// return value checked.
226 ///
227 /// # Examples
228 ///
229 /// ```
230 /// # extern crate libc;
231 /// # #[macro_use] extern crate vmm_sys_util;
232 /// #
233 /// # use libc::{open, O_CLOEXEC, O_RDWR};
234 /// # use std::fs::File;
235 /// # use std::os::raw::{c_char, c_uint};
236 /// # use std::os::unix::io::FromRawFd;
237 /// use vmm_sys_util::ioctl::ioctl;
238 ///
239 /// const KVMIO: c_uint = 0xAE;
240 /// const KVM_API_VERSION: u32 = 12;
241 /// ioctl_io_nr!(KVM_GET_API_VERSION, KVMIO, 0x00);
242 ///
243 /// let open_flags = O_RDWR | O_CLOEXEC;
244 /// let kvm_fd = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) };
245 ///
246 /// let ret = unsafe { ioctl(&File::from_raw_fd(kvm_fd), KVM_GET_API_VERSION()) };
247 ///
248 /// assert_eq!(ret as u32, KVM_API_VERSION);
249 /// ```
ioctl<F: AsRawFd>(fd: &F, req: c_ulong) -> c_int250 pub unsafe fn ioctl<F: AsRawFd>(fd: &F, req: c_ulong) -> c_int {
251     libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, 0)
252 }
253 
254 /// Run an [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html)
255 /// with a single value argument.
256 ///
257 /// # Arguments
258 ///
259 /// * `fd`: an open file descriptor corresponding to the device on which
260 /// to call the ioctl.
261 /// * `req`: a device-dependent request code.
262 /// * `arg`: a single value passed to ioctl.
263 ///
264 /// # Safety
265 ///
266 /// The caller should ensure to pass a valid file descriptor and have the
267 /// return value checked.
268 ///
269 /// # Examples
270 ///
271 /// ```
272 /// # extern crate libc;
273 /// # #[macro_use] extern crate vmm_sys_util;
274 /// # use libc::{open, O_CLOEXEC, O_RDWR};
275 /// # use std::fs::File;
276 /// # use std::os::raw::{c_char, c_uint, c_ulong};
277 /// # use std::os::unix::io::FromRawFd;
278 /// use vmm_sys_util::ioctl::ioctl_with_val;
279 ///
280 /// const KVMIO: c_uint = 0xAE;
281 /// const KVM_CAP_USER_MEMORY: u32 = 3;
282 /// ioctl_io_nr!(KVM_CHECK_EXTENSION, KVMIO, 0x03);
283 ///
284 /// let open_flags = O_RDWR | O_CLOEXEC;
285 /// let kvm_fd = unsafe { open("/dev/kvm\0".as_ptr() as *const c_char, open_flags) };
286 ///
287 /// let ret = unsafe {
288 ///     ioctl_with_val(
289 ///         &File::from_raw_fd(kvm_fd),
290 ///         KVM_CHECK_EXTENSION(),
291 ///         KVM_CAP_USER_MEMORY as c_ulong,
292 ///     )
293 /// };
294 /// assert!(ret > 0);
295 /// ```
ioctl_with_val<F: AsRawFd>(fd: &F, req: c_ulong, arg: c_ulong) -> c_int296 pub unsafe fn ioctl_with_val<F: AsRawFd>(fd: &F, req: c_ulong, arg: c_ulong) -> c_int {
297     libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg)
298 }
299 
300 /// Run an [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html)
301 /// with an immutable reference.
302 ///
303 /// # Arguments
304 ///
305 /// * `fd`: an open file descriptor corresponding to the device on which
306 /// to call the ioctl.
307 /// * `req`: a device-dependent request code.
308 /// * `arg`: an immutable reference passed to ioctl.
309 ///
310 /// # Safety
311 ///
312 /// The caller should ensure to pass a valid file descriptor and have the
313 /// return value checked.
ioctl_with_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &T) -> c_int314 pub unsafe fn ioctl_with_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &T) -> c_int {
315     libc::ioctl(
316         fd.as_raw_fd(),
317         req as IoctlRequest,
318         arg as *const T as *const c_void,
319     )
320 }
321 
322 /// Run an [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html)
323 /// with a mutable reference.
324 ///
325 /// # Arguments
326 ///
327 /// * `fd`: an open file descriptor corresponding to the device on which
328 /// to call the ioctl.
329 /// * `req`: a device-dependent request code.
330 /// * `arg`: a mutable reference passed to ioctl.
331 ///
332 /// # Safety
333 ///
334 /// The caller should ensure to pass a valid file descriptor and have the
335 /// return value checked.
ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> c_int336 pub unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> c_int {
337     libc::ioctl(
338         fd.as_raw_fd(),
339         req as IoctlRequest,
340         arg as *mut T as *mut c_void,
341     )
342 }
343 
344 /// Run an [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html)
345 /// with a raw pointer.
346 ///
347 /// # Arguments
348 ///
349 /// * `fd`: an open file descriptor corresponding to the device on which
350 /// to call the ioctl.
351 /// * `req`: a device-dependent request code.
352 /// * `arg`: a raw pointer passed to ioctl.
353 ///
354 /// # Safety
355 ///
356 /// The caller should ensure to pass a valid file descriptor and have the
357 /// return value checked.
ioctl_with_ptr<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: *const T) -> c_int358 pub unsafe fn ioctl_with_ptr<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: *const T) -> c_int {
359     libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *const c_void)
360 }
361 
362 /// Run an [`ioctl`](http://man7.org/linux/man-pages/man2/ioctl.2.html)
363 /// with a mutable raw pointer.
364 ///
365 /// # Arguments
366 ///
367 /// * `fd`: an open file descriptor corresponding to the device on which
368 /// to call the ioctl.
369 /// * `req`: a device-dependent request code.
370 /// * `arg`: a mutable raw pointer passed to ioctl.
371 ///
372 /// # Safety
373 ///
374 /// The caller should ensure to pass a valid file descriptor and have the
375 /// return value checked.
ioctl_with_mut_ptr<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: *mut T) -> c_int376 pub unsafe fn ioctl_with_mut_ptr<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: *mut T) -> c_int {
377     libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *mut c_void)
378 }
379 
380 #[cfg(test)]
381 mod tests {
382     const TUNTAP: ::std::os::raw::c_uint = 0x54;
383     const VHOST: ::std::os::raw::c_uint = 0xAF;
384     const EVDEV: ::std::os::raw::c_uint = 0x45;
385 
386     const KVMIO: ::std::os::raw::c_uint = 0xAE;
387 
388     ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01);
389     ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint);
390     ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int);
391     ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01);
392     ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, ::std::os::raw::c_int);
393     ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x2, ::std::os::raw::c_int);
394 
395     ioctl_ior_nr!(EVIOCGBIT, EVDEV, 0x20 + evt, [u8; 128], evt);
396     ioctl_io_nr!(FAKE_IOCTL_2_ARG, EVDEV, 0x01 + x + y, x, y);
397 
398     #[test]
test_ioctl_macros()399     fn test_ioctl_macros() {
400         assert_eq!(0x0000_AE01, KVM_CREATE_VM());
401         assert_eq!(0x0000_AF01, VHOST_SET_OWNER());
402         assert_eq!(0x8004_54CF, TUNGETFEATURES());
403         assert_eq!(0x4004_54D9, TUNSETQUEUE());
404         assert_eq!(0xC004_AE02, KVM_GET_MSR_INDEX_LIST());
405         assert_eq!(0xC004_AF12, VHOST_GET_VRING_BASE());
406 
407         assert_eq!(0x8080_4522, EVIOCGBIT(2));
408         assert_eq!(0x0000_4509, FAKE_IOCTL_2_ARG(3, 5));
409     }
410 }
411