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;