microsoft/openvmm

Public

mirrored from https://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/apply-async-process-wait-functionality

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

openhcl/hcl/src/protocol.rs

312lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Structures and definitions used between the underhill kernel and VMM.
5
6#![expect(non_camel_case_types, missing_docs)]
7
8use bitfield_struct::bitfield;
9use hvdef::HV_MESSAGE_SIZE;
10use hvdef::hypercall::HvInputVtl;
11use libc::c_void;
12use zerocopy::FromBytes;
13use zerocopy::Immutable;
14use zerocopy::IntoBytes;
15use zerocopy::KnownLayout;
16
17#[repr(C)]
18#[derive(Copy, Clone, Debug, Default)]
19pub struct hcl_translate_address_info {
20 pub gva_pfn: u64,
21 pub gpa_pfn: u64,
22}
23
24#[repr(C)]
25#[derive(Copy, Clone, Debug, Default)]
26pub struct hcl_signal_event_direct_t {
27 pub vp: u32,
28 pub flag: u16,
29 pub sint: u8,
30 pub vtl: u8,
31 pub pad: u32,
32 pub pad1: u16,
33 pub pad2: u8,
34 pub newly_signaled: u8,
35}
36
37pub const HV_VP_ASSIST_PAGE_SIGNAL_EVENT_COUNT: usize = 16;
38pub const HV_VP_ASSIST_PAGE_ACTION_TYPE_SIGNAL_EVENT: u64 = 1;
39
40#[repr(C)]
41#[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
42pub struct hv_vp_assist_page_signal_event {
43 pub action_type: u64,
44 pub vp: u32,
45 pub vtl: u8,
46 pub sint: u8,
47 pub flag: u16,
48}
49
50#[repr(C)]
51#[derive(Copy, Clone, Debug)]
52pub struct hcl_post_message_direct_t {
53 pub vp: u32,
54 pub sint: u32,
55 pub pad: u32,
56 pub pad2: u8,
57 pub vtl: u8,
58 pub size: u16,
59 pub message: *const u8,
60}
61
62#[repr(C, packed)]
63#[derive(Copy, Clone, Debug, Default)]
64pub struct hcl_pfn_range_t {
65 pub start_pfn: u64,
66 pub last_pfn: u64,
67}
68
69#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
70#[repr(C)]
71pub struct hcl_cpu_context_x64 {
72 // These are in the canonical order, _except_ it doesn't contain
73 // RSP--CR2 is stored where RSP would normally be.
74 pub gps_no_rsp: [u64; 16],
75 pub fx_state: x86defs::xsave::Fxsave,
76 pub reserved: [u8; 384],
77}
78
79const _: () = assert!(size_of::<hcl_cpu_context_x64>() == 1024);
80
81#[derive(FromBytes, IntoBytes, Immutable, KnownLayout)]
82#[repr(C)]
83// NOTE: x18 is managed by the hypervisor. It is assumed here be available
84// for easier offset arithmetic.
85pub struct hcl_cpu_context_aarch64 {
86 pub x: [u64; 31],
87 pub _rsvd: u64,
88 pub q: [u128; 32],
89 pub reserved: [u8; 256],
90}
91
92const _: () = assert!(size_of::<hcl_cpu_context_aarch64>() == 1024);
93
94pub const RAX: usize = 0;
95pub const RCX: usize = 1;
96pub const RDX: usize = 2;
97pub const RBX: usize = 3;
98pub const CR2: usize = 4; // RSP on TdxL2EnterGuestState, CR2 on hcl_cpu_context_x64
99pub const RBP: usize = 5;
100pub const RSI: usize = 6;
101pub const RDI: usize = 7;
102pub const R8: usize = 8;
103pub const R9: usize = 9;
104pub const R10: usize = 10;
105pub const R11: usize = 11;
106pub const R12: usize = 12;
107pub const R13: usize = 13;
108pub const R14: usize = 14;
109pub const R15: usize = 15;
110
111pub const VTL_RETURN_ACTION_SIZE: usize = 256;
112
113/// Kernel IPI offloading flags
114#[bitfield(u8)]
115#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
116pub struct hcl_intr_offload_flags {
117 /// Enable the base level of kernel offloading support. Requires vAPIC to be enabled.
118 /// HLT and Idle are accelerated by the kernel. When halted, an interrupt may be injected
119 /// entirely in kernel, bypassing user-space.
120 pub offload_intr_inject: bool,
121 /// Handle the X2 APIC ICR register in kernel
122 pub offload_x2apic: bool,
123 #[bits(3)]
124 reserved: u8,
125 /// Halt, due to other reason. Kernel cannot clear this state.
126 pub halted_other: bool,
127 /// Halt, due to HLT instruction. Kernel can clear this state.
128 pub halted_hlt: bool,
129 /// Halt, due to guest idle. Kernel can clear this state.
130 pub halted_idle: bool,
131}
132
133#[repr(C)]
134pub struct hcl_run {
135 pub cancel: u32,
136 pub vtl_ret_action_size: u32,
137 pub flags: u32,
138 pub scan_proxy_irr: u8,
139 pub offload_flags: hcl_intr_offload_flags,
140 pub pad: [u8; 1],
141 pub mode: EnterModes,
142 pub exit_message: [u8; HV_MESSAGE_SIZE],
143 pub context: [u8; 1024],
144 pub vtl_ret_actions: [u8; VTL_RETURN_ACTION_SIZE],
145 pub proxy_irr: [u32; 8],
146 pub target_vtl: HvInputVtl,
147 pub proxy_irr_blocked: [u32; 8],
148 pub proxy_irr_exit: [u32; 8],
149}
150
151// The size of hcl_run must be less than or equal to a single 4K page.
152const _: () = assert!(size_of::<hcl_run>() <= 4096);
153
154pub const MSHV_VTL_RUN_FLAG_HALTED: u32 = 1 << 0;
155
156#[repr(C)]
157pub struct hcl_set_poll_file {
158 pub cpu: i32,
159 pub fd: i32,
160}
161
162#[repr(C)]
163pub struct hcl_hvcall_setup {
164 pub allow_bitmap_size: u64,
165 pub allow_bitmap_ptr: *const u64,
166}
167
168#[repr(C)]
169pub struct hcl_hvcall {
170 pub control: hvdef::hypercall::Control,
171 pub input_size: usize,
172 pub input_data: *const c_void,
173 pub status: hvdef::hypercall::HypercallOutput,
174 pub output_size: usize,
175 pub output_data: *const c_void,
176}
177
178pub const HCL_REG_PAGE_OFFSET: i64 = 1 << 16;
179pub const HCL_VMSA_PAGE_OFFSET: i64 = 2 << 16;
180pub const MSHV_APIC_PAGE_OFFSET: i64 = 3 << 16;
181pub const HCL_VMSA_GUEST_VSM_PAGE_OFFSET: i64 = 4 << 16;
182
183open_enum::open_enum! {
184 /// 4 bits represent VTL0 enter mode.
185 pub enum EnterMode: u8 {
186 /// "Fast" mode: Enters VTL0 with scheduler ticks on, no extra cost on turning off the scheduler
187 /// timers, therefore it's fast.
188 FAST = 0,
189 /// "Play idle" mode: Enters VTL0 with scheduler ticks off (setting the current kernel thread to
190 /// idle).
191 PLAY_IDLE = 1,
192 /// "Idle to VTL0 idle" mode: Switches to the idle thread, and the idle thread enters VTL0 with
193 /// scheduler ticks off.
194 IDLE_TO_VTL0 = 2,
195 }
196}
197
198impl EnterMode {
199 const fn into_bits(self) -> u8 {
200 self.0
201 }
202
203 const fn from_bits(bits: u8) -> Self {
204 Self(bits)
205 }
206}
207
208/// Controls how to enter VTL0.
209#[bitfield(u8)]
210pub struct EnterModes {
211 /// [`Mode`] used when entering VTL0 the first time.
212 #[bits(4)]
213 pub first: EnterMode,
214 /// [`Mode`] used when interrupted from the previous enter to VTL0.
215 #[bits(4)]
216 pub second: EnterMode,
217}
218
219/// The register values returned from a TDG.VP.ENTER call. These are readable
220/// via mmaping the mshv_vtl driver inside `hcl_run`, and returned on a run_vp
221/// ioctl exit. See the TDX ABI specification for output operands for
222/// TDG.VP.ENTER.
223#[repr(C)]
224#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
225pub struct tdx_tdg_vp_enter_exit_info {
226 pub rax: u64,
227 pub rcx: u64,
228 pub rdx: u64,
229 pub rsi: u64,
230 pub rdi: u64,
231 pub r8: u64,
232 pub r9: u64,
233 pub r10: u64,
234 pub r11: u64,
235 pub r12: u64,
236 pub r13: u64,
237}
238
239#[bitfield(u64)]
240#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
241pub struct tdx_vp_state_flags {
242 /// Issue a cache flush for a WBINVD before calling VP.ENTER.
243 pub wbinvd: bool,
244 /// Issue a cache flush for a WBNOINVD before calling VP.ENTER.
245 pub wbnoinvd: bool,
246 #[bits(62)]
247 reserved: u64,
248}
249
250/// Additional VP state that is save/restored across TDG.VP.ENTER.
251#[repr(C)]
252#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
253pub struct tdx_vp_state {
254 pub msr_kernel_gs_base: u64,
255 pub msr_star: u64,
256 pub msr_lstar: u64,
257 pub msr_sfmask: u64,
258 pub msr_xss: u64,
259 pub cr2: u64,
260 pub msr_tsc_aux: u64,
261 pub flags: tdx_vp_state_flags,
262}
263
264/// L2 TSC deadline state for current VP.
265#[repr(C)]
266#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
267pub struct tdx_l2_tsc_deadline_state {
268 /// Timer deadline value in absolute virtual TSC units for this processor.
269 pub deadline: u64,
270 /// Controls whether the TSC deadline should be updated.
271 ///
272 /// When set to 1, the `mshv_vtl` driver will issue a `TDG.VP.WR` call to
273 /// update the TSC deadline during the next entry into lower VTL.
274 pub update_deadline: u8,
275 pub pad: [u8; 7],
276}
277
278#[repr(C)]
279#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
280pub struct tdx_vp_context {
281 pub exit_info: tdx_tdg_vp_enter_exit_info,
282 pub pad1: [u8; 48],
283 pub vp_state: tdx_vp_state,
284 pub pad2: [u8; 32],
285 pub entry_rcx: x86defs::tdx::TdxVmFlags,
286 pub gpr_list: x86defs::tdx::TdxL2EnterGuestState,
287 pub l2_tsc_deadline: tdx_l2_tsc_deadline_state,
288 pub pad3: [u8; 80],
289 pub fx_state: x86defs::xsave::Fxsave,
290 pub pad4: [u8; 16],
291}
292
293const _: () = assert!(core::mem::offset_of!(tdx_vp_context, gpr_list) + 272 == 512);
294const _: () = assert!(size_of::<tdx_vp_context>() == 1024);
295
296#[bitfield(u64)]
297#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
298pub struct hcl_kick_cpus_flags {
299 #[bits(1)]
300 pub wait_for_other_cpus: bool,
301 #[bits(1)]
302 pub cancel_run: bool,
303 #[bits(62)]
304 reserved: u64,
305}
306
307#[repr(C)]
308pub struct hcl_kick_cpus {
309 pub len: u64,
310 pub cpu_mask: *const u8,
311 pub flags: hcl_kick_cpus_flags,
312}
313