Skip to main content

mshv_ioctls/
lib.rs

1// Copyright © 2020, Microsoft Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4//
5#![allow(non_snake_case)]
6#![allow(non_upper_case_globals)]
7#![allow(non_camel_case_types)]
8#![deny(missing_docs)]
9#![allow(clippy::doc_lazy_continuation)]
10
11//! A safe wrapper around the kernel's MSHV interface.
12//!
13//! This crate offers safe wrappers for:
14//! - [system ioctls](struct.Mshv.html) using the `Mshv` structure
15//! - [VM ioctls](struct.VmFd.html) using the `VmFd` structure
16//! - [vCPU ioctls](struct.VcpuFd.html) using the `VcpuFd` structure
17//! - [device ioctls](struct.DeviceFd.html) using the `DeviceFd` structure
18//!
19//! # Platform support
20//!
21//! - x86_64
22//!
23//! **NOTE:** The list of available ioctls is not extensive.
24//!
25//! # Example - Running a VM on x86_64
26//!
27//! In this example we are creating a Virtual Machine (VM) with one vCPU.
28//! On the vCPU we are running machine specific code. This example is based on
29//! the [LWN article](https://lwn.net/Articles/658511/) on using the MSHV API.
30//!
31//! To get code running on the vCPU we are going through the following steps:
32//!
33//! 1. Instantiate MSHV. This is used for running
34//!    [system specific ioctls](struct.Mshv.html).
35//! 2. Use the MSHV object to create a VM. The VM is used for running
36//!    [VM specific ioctls](struct.VmFd.html).
37//! 3. Initialize the guest memory for the created VM. In this dummy example we
38//!    are adding only one memory region and write the code in one memory page.
39//! 4. Create a vCPU using the VM object. The vCPU is used for running
40//!    [vCPU specific ioctls](struct.VcpuFd.html).
41//! 5. Setup architectural specific general purpose registers and special registers. For
42//!    details about how and why these registers are set, please check the
43//!    [LWN article](https://lwn.net/Articles/658511/) on which this example is
44//!    built.
45//! 6. Run the vCPU code in a loop and check the
46//!    [exit reasons](enum.VcpuExit.html).
47//!
48//!
49//! ```ignore
50//! use crate::ioctls::system::Mshv;
51//! use std::io::Write;
52//! use libc::c_void;
53//!
54//! fn run_vm() {
55//!     let mshv = Mshv::new().unwrap();
56//!     let vm = mshv.create_vm().unwrap();
57//!     let vcpu = vm.create_vcpu(0).unwrap();
58//!     // This example is based on https://lwn.net/Articles/658511/
59//!     #[rustfmt::skip]
60//!     let code:[u8;11] = [
61//!         0xba, 0xf8, 0x03,  /* mov $0x3f8, %dx */
62//!         0x00, 0xd8,         /* add %bl, %al */
63//!         0x04, b'0',         /* add $'0', %al */
64//!         0xee,               /* out %al, (%dx) */
65//!         /* send a 0 to indicate we're done */
66//!         0xb0, b'\0',        /* mov $'\0', %al */
67//!         0xee,               /* out %al, (%dx) */
68//!     ];
69//!
70//!     let mem_size = 0x4000;
71//!     // SAFETY: FFI call.
72//!     let load_addr = unsafe {
73//!         libc::mmap(
74//!             std::ptr::null_mut(),
75//!             mem_size,
76//!             libc::PROT_READ | libc::PROT_WRITE,
77//!             libc::MAP_ANONYMOUS | libc::MAP_SHARED | libc::MAP_NORESERVE,
78//!             -1,
79//!             0,
80//!         )
81//!     } as *mut u8;
82//!     let mem_region = mshv_user_mem_region {
83//!         flags: set_bits!(u8, MSHV_SET_MEM_BIT_WRITABLE, MSHV_SET_MEM_BIT_EXECUTABLE),
84//!         guest_pfn: 0x1,
85//!         size: 0x1000,
86//!         userspace_addr: load_addr as u64,
87//!         ..Default::default()
88//!     };
89//!
90//!     vm.map_user_memory(mem_region).unwrap();
91//!
92//!     // SAFETY: load_addr is a valid pointer from mmap. Its length is mem_size.
93//!     unsafe {
94//!         // Get a mutable slice of `mem_size` from `load_addr`.
95//!         let mut slice = slice::from_raw_parts_mut(load_addr, mem_size);
96//!         slice.write_all(&code).unwrap();
97//!     }
98//!
99//!     //Get CS Register
100//!     let mut cs_reg = hv_register_assoc {
101//!         name: hv_register_name::HV_X64_REGISTER_CS as u32,
102//!         ..Default::default()
103//!     };
104//!     vcpu.get_reg(slice::from_mut(&mut cs_reg)).unwrap();
105//!
106//!     // SAFETY: access union fields
107//!     unsafe {
108//!         assert_ne!({ cs_reg.value.segment.base }, 0);
109//!         assert_ne!({ cs_reg.value.segment.selector }, 0);
110//!     };
111//!
112//!     cs_reg.value.segment.base = 0;
113//!     cs_reg.value.segment.selector = 0;
114//!
115//!     vcpu.set_reg(&[
116//!         cs_reg,
117//!         hv_register_assoc {
118//!             name: hv_register_name::HV_X64_REGISTER_RAX as u32,
119//!             value: hv_register_value { reg64: 2 },
120//!             ..Default::default()
121//!         },
122//!         hv_register_assoc {
123//!             name: hv_register_name::HV_X64_REGISTER_RBX as u32,
124//!             value: hv_register_value { reg64: 2 },
125//!             ..Default::default()
126//!         },
127//!         hv_register_assoc {
128//!             name: hv_register_name::HV_X64_REGISTER_RIP as u32,
129//!             value: hv_register_value { reg64: 0x1000 },
130//!             ..Default::default()
131//!         },
132//!         hv_register_assoc {
133//!             name: hv_register_name::HV_X64_REGISTER_RFLAGS as u32,
134//!             value: hv_register_value { reg64: 0x2 },
135//!             ..Default::default()
136//!         },
137//!     ])
138//!     .unwrap();
139//!
140//!     let hv_message: hv_message = Default::default();
141//!     let mut done = false;
142//!     loop {
143//!         let ret_hv_message: hv_message = vcpu.run(hv_message).unwrap();
144//!         match ret_hv_message.header.message_type {
145//!             hv_message_type_HVMSG_X64_HALT => {
146//!                 println!("VM Halted!");
147//!                 break;
148//!             }
149//!             hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT => {
150//!                 let io_message = ret_hv_message.to_ioport_info().unwrap();
151//!
152//!                 if !done {
153//!                     assert!(io_message.rax == b'4' as u64);
154//!                     assert!(io_message.port_number == 0x3f8);
155//!                     // SAFETY: access union fields.
156//!                     unsafe {
157//!                         assert!(io_message.access_info.__bindgen_anon_1.string_op() == 0);
158//!                         assert!(io_message.access_info.__bindgen_anon_1.access_size() == 1);
159//!                     }
160//!                     assert!(
161//!                         io_message.header.intercept_access_type == /*HV_INTERCEPT_ACCESS_WRITE*/ 1_u8
162//!                     );
163//!                     done = true;
164//!                     /* Advance rip */
165//!                     vcpu.set_reg(&[hv_register_assoc {
166//!                         name: hv_register_name::HV_X64_REGISTER_RIP as u32,
167//!                         value: hv_register_value {
168//!                             reg64: io_message.header.rip + 1,
169//!                         },
170//!                         ..Default::default()
171//!                     }])
172//!                     .unwrap();
173//!                 } else {
174//!                     assert!(io_message.rax == b'\0' as u64);
175//!                     assert!(io_message.port_number == 0x3f8);
176//!                     // SAFETY: access union fields.
177//!                     unsafe {
178//!                         assert!(io_message.access_info.__bindgen_anon_1.string_op() == 0);
179//!                         assert!(io_message.access_info.__bindgen_anon_1.access_size() == 1);
180//!                     }
181//!                     assert!(
182//!                         io_message.header.intercept_access_type == /*HV_INTERCEPT_ACCESS_WRITE*/ 1_u8
183//!                     );
184//!                     break;
185//!                 }
186//!             }
187//!             _ => {
188//!                 println!("Message type: 0x{:x?}", {
189//!                     ret_hv_message.header.message_type
190//!                 });
191//!                 panic!("Unexpected Exit Type");
192//!             }
193//!         };
194//!     }
195//!     assert!(done);
196//!     vm.unmap_user_memory(mem_region).unwrap();
197//!     // SAFETY: FFI call. We're sure load_addr and mem_size are correct.
198//!     unsafe { libc::munmap(load_addr as *mut c_void, mem_size) };
199//! }
200//! ```
201
202#[macro_use]
203mod ioctls;
204pub use ioctls::device::DeviceFd;
205#[deprecated(note = "Use Mshv::make_default_partition_create_arg instead")]
206pub use ioctls::system::make_default_partition_create_arg;
207pub use ioctls::system::make_default_synthetic_features_mask;
208pub use ioctls::system::Mshv;
209pub use ioctls::vcpu::VcpuFd;
210pub use ioctls::vm::InterruptRequest;
211pub use ioctls::vm::IoEventAddress;
212pub use ioctls::vm::NoDatamatch;
213pub use ioctls::vm::VmFd;
214pub use ioctls::vm::VmType;
215pub use ioctls::MshvError;
216
217#[macro_use]
218mod mshv_ioctls;
219pub use mshv_ioctls::*;
220
221#[macro_use]
222extern crate vmm_sys_util;