vmm_sys_util/linux/ioctl.rs
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
12use std::os::raw::{c_int, c_uint, c_ulong, c_void};
13use 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/// ```
27pub 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]
50macro_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]
76macro_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]
93macro_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]
123macro_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]
153macro_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)]
179mod 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}
203pub 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")]
208type IoctlRequest = c_int;
209#[cfg(all(not(target_env = "musl"), not(target_os = "android")))]
210type IoctlRequest = c_ulong;
211#[cfg(all(not(target_env = "musl"), target_os = "android"))]
212type 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/// ```
250pub 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/// ```
296pub 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.
314pub 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.
336pub 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.
358pub 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.
376pub 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)]
381mod 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]
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}