1#![expect(missing_docs)]
5#![cfg(target_os = "linux")]
6#![expect(unsafe_code)]
9
10pub use kvm_bindings::kvm_ioeventfd_flag_nr_datamatch;
11pub use kvm_bindings::kvm_ioeventfd_flag_nr_deassign;
12pub use kvm_bindings::*;
13use pal::unix::pthread::*;
14use parking_lot::RwLock;
15use std::fs::File;
16use std::io;
17use std::marker::PhantomData;
18use std::os::unix::prelude::*;
19use std::sync::Once;
20use std::sync::atomic::AtomicU8;
21use std::sync::atomic::AtomicUsize;
22use std::sync::atomic::Ordering;
23use thiserror::Error;
24
25mod ioctl {
26 #[cfg(target_arch = "aarch64")]
27 use super::KvmArmRmiPopulate;
28 use kvm_bindings::*;
29 #[cfg(target_arch = "x86_64")]
30 use nix::errno::Errno;
31 use nix::ioctl_read;
32 use nix::ioctl_readwrite;
33 use nix::ioctl_readwrite_bad;
34 use nix::ioctl_write_int_bad;
35 use nix::ioctl_write_ptr;
36 use nix::request_code_none;
37 use nix::request_code_readwrite;
38 use std::mem::size_of;
39
40 const KVMIO: u8 = 0xae;
41
42 ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x1));
43 ioctl_write_int_bad!(kvm_check_extension, request_code_none!(KVMIO, 0x03));
44 ioctl_write_int_bad!(kvm_get_vcpu_mmap_size, request_code_none!(KVMIO, 0x04));
45 #[cfg(target_arch = "x86_64")]
46 ioctl_readwrite!(kvm_get_supported_cpuid, KVMIO, 0x05, kvm_cpuid2);
47 #[cfg(target_arch = "x86_64")]
48 ioctl_readwrite!(kvm_get_supported_hv_cpuid, KVMIO, 0xc1, kvm_cpuid2);
49 ioctl_write_int_bad!(kvm_create_vcpu, request_code_none!(KVMIO, 0x41));
50 ioctl_write_ptr!(
51 kvm_set_user_memory_region,
52 KVMIO,
53 0x46,
54 kvm_userspace_memory_region
55 );
56 ioctl_write_ptr!(
57 kvm_set_user_memory_region2,
58 KVMIO,
59 0x49,
60 kvm_userspace_memory_region2
61 );
62 ioctl_write_ptr!(kvm_irq_line, KVMIO, 0x61, kvm_irq_level);
63 ioctl_write_ptr!(kvm_set_gsi_routing, KVMIO, 0x6a, kvm_irq_routing);
64 ioctl_write_ptr!(kvm_irqfd, KVMIO, 0x76, kvm_irqfd);
65 ioctl_write_int_bad!(kvm_set_boot_cpu_id, request_code_none!(KVMIO, 0x78));
66 ioctl_write_ptr!(kvm_set_clock, KVMIO, 0x7b, kvm_clock_data);
67 ioctl_read!(kvm_get_clock, KVMIO, 0x7c, kvm_clock_data);
68 ioctl_write_int_bad!(kvm_run, request_code_none!(KVMIO, 0x80));
69 #[cfg(not(target_arch = "aarch64"))]
71 ioctl_read!(kvm_get_regs, KVMIO, 0x81, kvm_regs);
72 #[cfg(not(target_arch = "aarch64"))]
74 ioctl_write_ptr!(kvm_set_regs, KVMIO, 0x82, kvm_regs);
75 ioctl_read!(kvm_get_sregs, KVMIO, 0x83, kvm_sregs);
76 ioctl_write_ptr!(kvm_set_sregs, KVMIO, 0x84, kvm_sregs);
77 ioctl_readwrite!(kvm_translation, KVMIO, 0x85, kvm_translation);
78 ioctl_write_ptr!(kvm_interrupt, KVMIO, 0x86, kvm_interrupt);
79 #[cfg(target_arch = "x86_64")]
80 ioctl_readwrite!(kvm_get_msrs, KVMIO, 0x88, kvm_msrs);
81 #[cfg(target_arch = "x86_64")]
82 ioctl_write_ptr!(kvm_set_msrs, KVMIO, 0x89, kvm_msrs);
83 ioctl_write_ptr!(kvm_set_signal_mask, KVMIO, 0x8b, kvm_signal_mask);
84 ioctl_read!(kvm_get_fpu, KVMIO, 0x8c, kvm_fpu);
85 ioctl_write_ptr!(kvm_set_fpu, KVMIO, 0x8d, kvm_fpu);
86 #[cfg(target_arch = "x86_64")]
87 ioctl_read!(kvm_get_lapic, KVMIO, 0x8e, kvm_lapic_state);
88 #[cfg(target_arch = "x86_64")]
89 ioctl_write_ptr!(kvm_set_lapic, KVMIO, 0x8f, kvm_lapic_state);
90 #[cfg(target_arch = "x86_64")]
91 ioctl_write_ptr!(kvm_set_cpuid2, KVMIO, 0x90, kvm_cpuid2);
92 ioctl_read!(kvm_get_mp_state, KVMIO, 0x98, kvm_mp_state);
93 ioctl_write_ptr!(kvm_set_mp_state, KVMIO, 0x99, kvm_mp_state);
94 ioctl_read!(kvm_get_vcpu_events, KVMIO, 0x9f, kvm_vcpu_events);
95 ioctl_write_ptr!(kvm_set_vcpu_events, KVMIO, 0xa0, kvm_vcpu_events);
96 #[cfg(target_arch = "x86_64")]
97 ioctl_read!(kvm_get_debugregs, KVMIO, 0xa1, kvm_debugregs);
98 #[cfg(target_arch = "x86_64")]
99 ioctl_write_ptr!(kvm_set_debugregs, KVMIO, 0xa2, kvm_debugregs);
100 ioctl_write_ptr!(kvm_enable_cap, KVMIO, 0xa3, kvm_enable_cap);
101 #[cfg(target_arch = "x86_64")]
102 ioctl_read!(kvm_get_xsave, KVMIO, 0xa4, kvm_xsave);
103 #[cfg(target_arch = "x86_64")]
104 ioctl_write_ptr!(kvm_set_xsave, KVMIO, 0xa5, kvm_xsave);
105 ioctl_write_ptr!(kvm_signal_msi, KVMIO, 0xa5, kvm_msi);
106 #[cfg(target_arch = "x86_64")]
107 ioctl_read!(kvm_get_xcrs, KVMIO, 0xa6, kvm_xcrs);
108 #[cfg(target_arch = "x86_64")]
109 ioctl_write_ptr!(kvm_set_xcrs, KVMIO, 0xa7, kvm_xcrs);
110 ioctl_write_ptr!(kvm_get_reg, KVMIO, 0xab, kvm_one_reg);
111 ioctl_write_ptr!(kvm_set_reg, KVMIO, 0xac, kvm_one_reg);
112 #[cfg(target_arch = "aarch64")]
113 ioctl_write_ptr!(kvm_arm_vcpu_init, KVMIO, 0xae, kvm_vcpu_init);
114 #[cfg(target_arch = "aarch64")]
115 ioctl_read!(kvm_arm_preferred_target, KVMIO, 0xaf, kvm_vcpu_init);
116 ioctl_write_ptr!(kvm_ioeventfd, KVMIO, 0x79, kvm_ioeventfd);
117 ioctl_write_ptr!(kvm_set_guest_debug, KVMIO, 0x9b, kvm_guest_debug);
118 #[cfg(target_arch = "x86_64")]
119 ioctl_write_ptr!(kvm_x86_setup_mce, KVMIO, 0x9c, u64);
120 #[cfg(target_arch = "x86_64")]
121 ioctl_read!(kvm_x86_get_mce_cap_supported, KVMIO, 0x9d, u64);
122 ioctl_write_ptr!(
123 kvm_set_memory_attributes,
124 KVMIO,
125 0xd2,
126 kvm_memory_attributes
127 );
128 ioctl_readwrite!(kvm_create_device, KVMIO, 0xe0, kvm_create_device);
129 ioctl_write_ptr!(kvm_set_device_attr, KVMIO, 0xe1, kvm_device_attr);
130 ioctl_readwrite!(kvm_create_guest_memfd, KVMIO, 0xd4, kvm_create_guest_memfd);
131 #[cfg(target_arch = "aarch64")]
132 ioctl_readwrite_bad!(
133 kvm_arm_rmi_populate,
134 request_code_readwrite!(KVMIO, 0xd7, size_of::<KvmArmRmiPopulate>()),
135 KvmArmRmiPopulate
136 );
137 #[cfg(target_arch = "x86_64")]
138 ioctl_readwrite_bad!(
139 kvm_memory_encrypt_op,
140 request_code_readwrite!(KVMIO, 0xba, size_of::<libc::c_ulong>()),
141 kvm_sev_cmd
142 );
143 #[cfg(target_arch = "x86_64")]
144 pub unsafe fn kvm_memory_encrypt_op_supported(fd: libc::c_int) -> nix::Result<()> {
148 match unsafe {
151 libc::ioctl(
152 fd,
153 request_code_readwrite!(KVMIO, 0xba, size_of::<libc::c_ulong>()),
154 std::ptr::null_mut::<libc::c_void>(),
155 )
156 } {
157 0 => Ok(()),
158 _ => Err(Errno::last()),
159 }
160 }
161}
162
163#[cfg(target_arch = "x86_64")]
164const KVM_CAP_VM_TYPES_UAPI: u32 = 235;
165#[cfg(target_arch = "x86_64")]
166const KVM_CAP_EXIT_HYPERCALL_UAPI: u32 = 201;
167#[cfg(target_arch = "x86_64")]
168pub const KVM_HC_MAP_GPA_RANGE_UAPI: u64 = 12;
169#[cfg(target_arch = "x86_64")]
170pub const KVM_MAP_GPA_RANGE_ENCRYPTED_UAPI: u64 = 1 << 4;
171#[cfg(target_arch = "x86_64")]
172pub const KVM_MAP_GPA_RANGE_DECRYPTED_UAPI: u64 = 0 << 4;
173#[cfg(target_arch = "x86_64")]
174const KVM_X86_SNP_VM_UAPI: libc::c_int = 4;
175
176pub const KVM_MEMORY_EXIT_FLAG_PRIVATE_UAPI: u64 = 1 << 3;
177
178#[cfg(target_arch = "aarch64")]
179pub const KVM_CAP_ARM_RMI_UAPI: u32 = 249;
180#[cfg(target_arch = "aarch64")]
181pub const KVM_ARM_RMI_POPULATE_FLAGS_MEASURE_UAPI: u32 = 1 << 0;
182#[cfg(target_arch = "aarch64")]
183const KVM_VM_TYPE_ARM_IPA_SIZE_MASK_UAPI: u64 = 0xff;
184#[cfg(target_arch = "aarch64")]
185const KVM_VM_TYPE_ARM_REALM_UAPI: u64 = 1 << 30;
186
187#[cfg(target_arch = "x86_64")]
188pub const KVM_SEV_SNP_PAGE_TYPE_NORMAL_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_NORMAL as u8;
189#[cfg(target_arch = "x86_64")]
190pub const KVM_SEV_SNP_PAGE_TYPE_ZERO_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_ZERO as u8;
191#[cfg(target_arch = "x86_64")]
192pub const KVM_SEV_SNP_PAGE_TYPE_UNMEASURED_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_UNMEASURED as u8;
193#[cfg(target_arch = "x86_64")]
194pub const KVM_SEV_SNP_PAGE_TYPE_SECRETS_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_SECRETS as u8;
195#[cfg(target_arch = "x86_64")]
196pub const KVM_SEV_SNP_PAGE_TYPE_CPUID_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_CPUID as u8;
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub enum VmType {
200 Default,
201 #[cfg(target_arch = "x86_64")]
202 Snp,
203 #[cfg(target_arch = "aarch64")]
204 Realm {
205 ipa_bits: u8,
206 },
207}
208
209#[cfg(target_arch = "x86_64")]
210#[derive(Debug, Copy, Clone, Eq, PartialEq)]
211pub enum SevSnpPageType {
212 Normal,
213 Zero,
214 Unmeasured,
215 Secrets,
216 Cpuid,
217}
218
219#[cfg(target_arch = "x86_64")]
220impl SevSnpPageType {
221 pub const fn as_uapi(self) -> u8 {
222 match self {
223 SevSnpPageType::Normal => KVM_SEV_SNP_PAGE_TYPE_NORMAL_UAPI,
224 SevSnpPageType::Zero => KVM_SEV_SNP_PAGE_TYPE_ZERO_UAPI,
225 SevSnpPageType::Unmeasured => KVM_SEV_SNP_PAGE_TYPE_UNMEASURED_UAPI,
226 SevSnpPageType::Secrets => KVM_SEV_SNP_PAGE_TYPE_SECRETS_UAPI,
227 SevSnpPageType::Cpuid => KVM_SEV_SNP_PAGE_TYPE_CPUID_UAPI,
228 }
229 }
230}
231
232#[cfg(target_arch = "aarch64")]
233#[repr(C)]
234#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
235pub struct KvmArmRmiPopulate {
236 pub base: u64,
237 pub size: u64,
238 pub source_uaddr: u64,
239 pub flags: u32,
240 pub reserved: u32,
241}
242
243#[derive(Error, Debug)]
244pub enum Error {
245 #[error("failed to open /dev/kvm")]
246 OpenKvm(#[source] io::Error),
247 #[error("SignalMsi")]
248 SignalMsi(#[source] nix::Error),
249 #[error("SetMemoryRegion")]
250 SetMemoryRegion(#[source] nix::Error),
251 #[error("SetMemoryAttributes")]
252 SetMemoryAttributes(#[source] nix::Error),
253 #[error("CreateGuestMemfd")]
254 CreateGuestMemfd(#[source] nix::Error),
255 #[error("CreateVm")]
256 CreateVm(#[source] nix::Error),
257 #[cfg(target_arch = "aarch64")]
258 #[error("ArmRmiPopulate")]
259 ArmRmiPopulate(#[source] nix::Error),
260 #[error("missing KVM capability: {0}")]
261 MissingCapability(&'static str),
262 #[error("unsupported KVM VM type: {0:?}")]
263 UnsupportedVmType(VmType),
264 #[cfg(target_arch = "x86_64")]
265 #[error("MemoryEncryptOp({command}, firmware_error={firmware_error:#x})")]
266 MemoryEncryptOp {
267 command: &'static str,
268 firmware_error: u32,
269 #[source]
270 source: nix::Error,
271 },
272 #[error("EnableCap({0})")]
273 EnableCap(&'static str, #[source] nix::Error),
274 #[error("CreateVCpu")]
275 CreateVCpu(#[source] nix::Error),
276 #[error("GetRegs")]
277 GetRegs(#[source] nix::Error),
278 #[error("GetSRegs")]
279 GetSRegs(#[source] nix::Error),
280 #[error("SetRegs")]
281 SetRegs(#[source] nix::Error),
282 #[error("SetSRegs")]
283 SetSRegs(#[source] nix::Error),
284 #[error("Run")]
285 Run(#[source] nix::Error),
286 #[error("RunMemoryFault(flags={flags:#x}, gpa={gpa:#x}, size={size:#x})")]
287 RunMemoryFault {
288 flags: u64,
289 gpa: u64,
290 size: u64,
291 #[source]
292 source: nix::Error,
293 },
294 #[error("GetVCpuMmapSize")]
295 GetVCpuMmapSize(#[source] nix::Error),
296 #[error("MmapVCpu")]
297 MmapVCpu(#[source] io::Error),
298 #[error("SetFpu")]
299 SetFpu(#[source] nix::Error),
300 #[error("GetSupportedCpuid")]
301 GetSupportedCpuid(#[source] nix::Error),
302 #[error("SetCpuid")]
303 SetCpuid(#[source] nix::Error),
304 #[error("Interrupt")]
305 Interrupt(#[source] nix::Error),
306 #[error("GetLApic")]
307 GetLApic(#[source] nix::Error),
308 #[error("SetLApic")]
309 SetLApic(#[source] nix::Error),
310 #[error("GetXsave")]
311 GetXsave(#[source] nix::Error),
312 #[error("SetXsave")]
313 SetXsave(#[source] nix::Error),
314 #[error("GetDebugRegs")]
315 GetDebugRegs(#[source] nix::Error),
316 #[error("SetDebugRegs")]
317 SetDebugRegs(#[source] nix::Error),
318 #[error("GetXcrs")]
319 GetXcrs(#[source] nix::Error),
320 #[error("SetXcrs")]
321 SetXcrs(#[source] nix::Error),
322 #[error("xsave is not enabled")]
323 XsaveNotEnabled,
324 #[error("SetGsiRouting")]
325 SetGsiRouting(#[source] nix::Error),
326 #[error("IrqLine")]
327 IrqLine(#[source] nix::Error),
328 #[error("GetMsrs")]
329 GetMsrs(#[source] nix::Error),
330 #[error("SetMsrs")]
331 SetMsrs(#[source] nix::Error),
332 #[error(
333 "MSR access only processed {completed} of {requested} entries (first failed MSR: {failed_msr:#x}, write={write})"
334 )]
335 IncompleteMsrs {
336 write: bool,
337 completed: usize,
338 requested: usize,
339 failed_msr: u32,
340 },
341 #[error("SetupMce")]
342 SetupMce(#[source] nix::Error),
343 #[error("GetMceCapSupported")]
344 GetMceCapSupported(#[source] nix::Error),
345 #[error("GetMpState")]
346 GetMpState(#[source] nix::Error),
347 #[error("SetMpState")]
348 SetMpState(#[source] nix::Error),
349 #[error("GetVcpuEvents")]
350 GetVcpuEvents(#[source] nix::Error),
351 #[error("SetVcpuEvents")]
352 SetVcpuEvents(#[source] nix::Error),
353 #[error("TranslateGva")]
354 TranslateGva(#[source] nix::Error),
355 #[error("unknown exit {0:#x}")]
356 UnknownExit(u32),
357 #[error("unknown Hyper-V exit {0:#x}")]
358 UnknownHvExit(u32),
359 #[error("ioeventfd")]
360 IoEventFd(#[source] nix::Error),
361 #[error("irqfd")]
362 IrqFd(#[source] nix::Error),
363 #[error("failed to set BSP")]
364 SetBsp(#[source] nix::Error),
365 #[error("CreateDevice")]
366 CreateDevice(#[source] nix::Error),
367 #[error("SetDeviceAttr")]
368 SetDeviceAttr(#[source] nix::Error),
369 #[error("CheckExtension")]
370 CheckExtension(#[source] nix::Error),
371 #[error("GetClock")]
372 GetClock(#[source] nix::Error),
373 #[error("SetClock")]
374 SetClock(#[source] nix::Error),
375}
376
377type Result<T, E = Error> = std::result::Result<T, E>;
378
379#[derive(Debug)]
380struct Vp {
381 vcpu: File,
382 run_data: VpPtr,
383 thread: RwLock<Option<Pthread>>,
384 _phantom: PhantomData<kvm_run>,
385}
386
387#[derive(Debug)]
389struct VpPtr {
390 ptr: *mut kvm_run,
391 len: usize,
392}
393
394unsafe impl Send for VpPtr {}
399unsafe impl Sync for VpPtr {}
401
402#[derive(Debug)]
404pub struct Kvm(File);
405
406impl Kvm {
407 pub fn new() -> Result<Self> {
409 let kvm = std::fs::OpenOptions::new()
410 .read(true)
411 .write(true)
412 .open("/dev/kvm")
413 .map_err(Error::OpenKvm)?;
414
415 Ok(Self(kvm))
416 }
417
418 #[cfg(target_arch = "x86_64")]
420 pub fn supported_cpuid(&self) -> Result<Vec<kvm_cpuid_entry2>> {
421 const MAX_CPUID_ENTRIES: usize = 256;
422 let mut supported_cpuid = Cpuid {
423 cpuid: kvm_cpuid2 {
424 nent: MAX_CPUID_ENTRIES as u32,
425 ..Default::default()
426 },
427 entries: [Default::default(); MAX_CPUID_ENTRIES],
428 };
429
430 unsafe {
433 ioctl::kvm_get_supported_cpuid(self.as_fd().as_raw_fd(), &mut supported_cpuid.cpuid)
434 .map_err(Error::GetSupportedCpuid)?;
435 }
436
437 Ok(supported_cpuid.entries[..supported_cpuid.cpuid.nent as usize].to_vec())
438 }
439
440 #[cfg(target_arch = "x86_64")]
444 pub fn supported_mce_cap(&self) -> Result<u64> {
445 let mut cap: u64 = 0;
446 unsafe {
448 ioctl::kvm_x86_get_mce_cap_supported(self.as_fd().as_raw_fd(), &mut cap)
449 .map_err(Error::GetMceCapSupported)?;
450 }
451 Ok(cap)
452 }
453
454 #[cfg(target_arch = "x86_64")]
457 pub fn supported_hv_cpuid(&self) -> Result<Vec<kvm_cpuid_entry2>> {
458 const MAX_CPUID_ENTRIES: usize = 256;
459 let mut supported_cpuid = Cpuid {
460 cpuid: kvm_cpuid2 {
461 nent: MAX_CPUID_ENTRIES as u32,
462 ..Default::default()
463 },
464 entries: [Default::default(); MAX_CPUID_ENTRIES],
465 };
466
467 unsafe {
469 ioctl::kvm_get_supported_hv_cpuid(self.as_fd().as_raw_fd(), &mut supported_cpuid.cpuid)
470 .map_err(Error::GetSupportedCpuid)?;
471 }
472
473 Ok(supported_cpuid.entries[..supported_cpuid.cpuid.nent as usize].to_vec())
474 }
475
476 pub fn check_extension(&self, extension: u32) -> nix::Result<libc::c_int> {
477 unsafe { ioctl::kvm_check_extension(self.as_fd().as_raw_fd(), extension as i32) }
479 }
480
481 pub fn new_vm(&self, vm_type: VmType) -> Result<Partition> {
482 let raw_vm_type = self.raw_vm_type(vm_type)?;
483 self.new_vm_with_type(raw_vm_type)
484 }
485
486 fn raw_vm_type(&self, vm_type: VmType) -> Result<libc::c_int> {
487 match vm_type {
488 VmType::Default => Ok(self.default_vm_type()),
489 #[cfg(target_arch = "x86_64")]
490 VmType::Snp => {
491 let supported_vm_types =
492 self.check_extension(KVM_CAP_VM_TYPES_UAPI)
493 .map_err(Error::CheckExtension)? as u64;
494 let raw_vm_type = KVM_X86_SNP_VM_UAPI;
495 let vm_type_bit = 1_u64
496 .checked_shl(raw_vm_type as u32)
497 .ok_or(Error::UnsupportedVmType(vm_type))?;
498 if supported_vm_types & vm_type_bit == 0 {
499 return Err(Error::UnsupportedVmType(vm_type));
500 }
501 Ok(raw_vm_type)
502 }
503 #[cfg(target_arch = "aarch64")]
504 VmType::Realm { ipa_bits } => Ok((KVM_VM_TYPE_ARM_REALM_UAPI
505 | ((ipa_bits as u64) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK_UAPI))
506 as libc::c_int),
507 }
508 }
509
510 fn default_vm_type(&self) -> libc::c_int {
511 #[cfg(target_arch = "aarch64")]
516 {
517 self.check_extension(KVM_CAP_ARM_VM_IPA_SIZE).unwrap_or(0)
518 }
519 #[cfg(not(target_arch = "aarch64"))]
520 {
521 0
522 }
523 }
524
525 fn new_vm_with_type(&self, vm_type: libc::c_int) -> Result<Partition> {
526 let vm = unsafe {
528 let fd =
529 ioctl::kvm_create_vm(self.as_fd().as_raw_fd(), vm_type).map_err(Error::CreateVm)?;
530 File::from_raw_fd(fd)
531 };
532
533 #[cfg(target_arch = "x86_64")]
537 unsafe {
538 ioctl::kvm_enable_cap(
541 vm.as_raw_fd(),
542 &kvm_enable_cap {
543 cap: KVM_CAP_DISABLE_QUIRKS,
544 args: [KVM_X86_QUIRK_LINT0_REENABLED.into(), 0, 0, 0],
545 ..Default::default()
546 },
547 )
548 .map_err(|err| Error::EnableCap("disable_quirks", err))?;
549 }
550
551 let mmap_size = unsafe {
553 ioctl::kvm_get_vcpu_mmap_size(self.as_fd().as_raw_fd(), 0)
554 .map_err(Error::GetVCpuMmapSize)? as usize
555 };
556
557 Ok(Partition {
558 vm,
559 vps: Vec::new(),
560 mmap_size,
561 })
562 }
563}
564
565impl AsFd for Kvm {
566 fn as_fd(&self) -> BorrowedFd<'_> {
567 self.0.as_fd()
568 }
569}
570
571impl From<File> for Kvm {
572 fn from(fd: File) -> Self {
573 Self(fd)
574 }
575}
576
577impl From<Kvm> for File {
578 fn from(kvm: Kvm) -> Self {
579 kvm.0
580 }
581}
582
583#[repr(C)]
584#[cfg(target_arch = "x86_64")]
585struct Cpuid {
586 cpuid: kvm_cpuid2,
587 entries: [kvm_cpuid_entry2; 256],
588}
589
590#[derive(Debug)]
591pub struct Partition {
592 vm: File,
593 vps: Vec<Option<Vp>>,
594 mmap_size: usize,
595}
596
597impl Partition {
598 #[cfg(target_arch = "x86_64")]
599 pub fn check_sev_snp_launch_extensions(&self) -> Result<()> {
600 unsafe { ioctl::kvm_memory_encrypt_op_supported(self.vm.as_raw_fd()) }.map_err(|err| {
603 Error::MemoryEncryptOp {
604 command: "KVM_MEMORY_ENCRYPT_OP(NULL)",
605 firmware_error: 0,
606 source: err,
607 }
608 })
609 }
610
611 #[cfg(target_arch = "x86_64")]
612 pub fn enable_hypercall_exits(&self, hypercall_mask: u64) -> Result<()> {
613 unsafe {
615 ioctl::kvm_enable_cap(
616 self.vm.as_raw_fd(),
617 &kvm_enable_cap {
618 cap: KVM_CAP_EXIT_HYPERCALL_UAPI,
619 args: [hypercall_mask, 0, 0, 0],
620 ..Default::default()
621 },
622 )
623 .map_err(|err| Error::EnableCap("exit_hypercall", err))?;
624 }
625 Ok(())
626 }
627
628 pub fn check_extension(&self, extension: u32) -> nix::Result<libc::c_int> {
629 unsafe { ioctl::kvm_check_extension(self.vm.as_raw_fd(), extension as i32) }
631 }
632
633 #[cfg(target_arch = "aarch64")]
634 pub fn arm_rmi_populate(&self, populate: &mut KvmArmRmiPopulate) -> Result<()> {
635 unsafe { ioctl::kvm_arm_rmi_populate(self.vm.as_raw_fd(), populate) }
638 .map_err(Error::ArmRmiPopulate)?;
639 Ok(())
640 }
641
642 #[cfg(target_arch = "x86_64")]
643 pub fn sev_snp_init(&self, sev: BorrowedFd<'_>) -> Result<()> {
644 let mut init = kvm_sev_init::default();
645 let mut command = kvm_sev_cmd {
646 id: sev_cmd_id_KVM_SEV_INIT2,
647 data: std::ptr::from_mut(&mut init) as u64,
648 sev_fd: sev.as_raw_fd() as u32,
649 ..Default::default()
650 };
651
652 unsafe {
655 ioctl::kvm_memory_encrypt_op(self.vm.as_raw_fd(), &mut command).map_err(|err| {
656 Error::MemoryEncryptOp {
657 command: "KVM_SEV_INIT2",
658 firmware_error: command.error,
659 source: err,
660 }
661 })?;
662 }
663 Ok(())
664 }
665
666 #[cfg(target_arch = "x86_64")]
667 fn sev_snp_cmd<T>(
668 &self,
669 sev: BorrowedFd<'_>,
670 command_name: &'static str,
671 command_id: sev_cmd_id,
672 data: &mut T,
673 ) -> Result<()> {
674 let mut command = kvm_sev_cmd {
675 id: command_id,
676 data: std::ptr::from_mut(data) as u64,
677 sev_fd: sev.as_raw_fd() as u32,
678 ..Default::default()
679 };
680
681 loop {
682 match unsafe { ioctl::kvm_memory_encrypt_op(self.vm.as_raw_fd(), &mut command) } {
685 Ok(_) => break,
686 Err(nix::errno::Errno::EAGAIN) => {}
687 Err(err) => {
688 return Err(Error::MemoryEncryptOp {
689 command: command_name,
690 firmware_error: command.error,
691 source: err,
692 });
693 }
694 }
695 }
696 Ok(())
697 }
698
699 #[cfg(target_arch = "x86_64")]
700 pub fn sev_snp_launch_start(
701 &self,
702 sev: BorrowedFd<'_>,
703 data: &mut kvm_sev_snp_launch_start,
704 ) -> Result<()> {
705 self.sev_snp_cmd(
706 sev,
707 "KVM_SEV_SNP_LAUNCH_START",
708 sev_cmd_id_KVM_SEV_SNP_LAUNCH_START,
709 data,
710 )
711 }
712
713 #[cfg(target_arch = "x86_64")]
714 pub fn sev_snp_launch_update(
715 &self,
716 sev: BorrowedFd<'_>,
717 gfn_start: u64,
718 uaddr: u64,
719 len: u64,
720 page_type: SevSnpPageType,
721 ) -> Result<()> {
722 let mut update = kvm_sev_snp_launch_update {
723 gfn_start,
724 uaddr,
725 len,
726 type_: page_type.as_uapi(),
727 ..Default::default()
728 };
729
730 while update.len != 0 {
731 self.sev_snp_cmd(
732 sev,
733 "KVM_SEV_SNP_LAUNCH_UPDATE",
734 sev_cmd_id_KVM_SEV_SNP_LAUNCH_UPDATE,
735 &mut update,
736 )?;
737 }
738 Ok(())
739 }
740
741 #[cfg(target_arch = "x86_64")]
742 pub fn sev_snp_launch_finish(
743 &self,
744 sev: BorrowedFd<'_>,
745 data: &mut kvm_sev_snp_launch_finish,
746 ) -> Result<()> {
747 self.sev_snp_cmd(
748 sev,
749 "KVM_SEV_SNP_LAUNCH_FINISH",
750 sev_cmd_id_KVM_SEV_SNP_LAUNCH_FINISH,
751 data,
752 )
753 }
754
755 pub fn enable_split_irqchip(&self, lines: u32) -> Result<()> {
756 unsafe {
760 ioctl::kvm_enable_cap(
761 self.vm.as_raw_fd(),
762 &kvm_enable_cap {
763 cap: KVM_CAP_SPLIT_IRQCHIP,
764 args: [lines.into(), 0, 0, 0],
765 ..Default::default()
766 },
767 )
768 .map_err(|err| Error::EnableCap("split_irqchip", err))?;
769 }
770 Ok(())
771 }
772
773 #[cfg(target_arch = "x86_64")]
775 pub fn enable_x2apic_api(&self) -> Result<()> {
776 let flags = KVM_X2APIC_API_USE_32BIT_IDS;
777 unsafe {
779 ioctl::kvm_enable_cap(
780 self.vm.as_raw_fd(),
781 &kvm_enable_cap {
782 cap: KVM_CAP_X2APIC_API,
783 args: [flags.into(), 0, 0, 0],
784 ..Default::default()
785 },
786 )
787 .map_err(|err| Error::EnableCap("x2apic_api", err))?;
788 }
789 Ok(())
790 }
791
792 pub fn enable_unknown_msr_exits(&self) -> Result<()> {
793 unsafe {
796 ioctl::kvm_enable_cap(
797 self.vm.as_raw_fd(),
798 &kvm_enable_cap {
799 cap: KVM_CAP_X86_USER_SPACE_MSR,
800 args: [KVM_MSR_EXIT_REASON_UNKNOWN.into(), 0, 0, 0],
801 ..Default::default()
802 },
803 )
804 .map_err(|err| Error::EnableCap("user_space_msr", err))?;
805 }
806 Ok(())
807 }
808
809 #[cfg(target_arch = "x86_64")]
812 pub fn set_bsp(&mut self, vcpu_idx: u32) -> Result<()> {
813 unsafe {
815 ioctl::kvm_set_boot_cpu_id(self.vm.as_raw_fd(), vcpu_idx as i32)
816 .map_err(Error::SetBsp)?;
817 }
818
819 Ok(())
820 }
821
822 pub fn add_vp(&mut self, vcpu_idx: u32) -> Result<()> {
823 let vcpu = unsafe {
826 let fd = ioctl::kvm_create_vcpu(self.vm.as_raw_fd(), vcpu_idx as i32)
827 .map_err(Error::CreateVCpu)?;
828 File::from_raw_fd(fd)
829 };
830
831 let ptr = unsafe {
833 let ptr = libc::mmap(
834 std::ptr::null_mut(),
835 self.mmap_size,
836 libc::PROT_READ | libc::PROT_WRITE,
837 libc::MAP_SHARED,
838 vcpu.as_raw_fd(),
839 0,
840 );
841 if ptr == libc::MAP_FAILED {
842 return Err(Error::MmapVCpu(io::Error::last_os_error()));
843 }
844 ptr
845 };
846
847 #[cfg(target_arch = "aarch64")]
848 {
849 let mut kvi = kvm_vcpu_init::default();
851 kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2;
852
853 if vcpu_idx > 0 {
854 kvi.features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF;
855 }
856
857 let mut pref_target = kvm_vcpu_init::default();
858 unsafe {
860 ioctl::kvm_arm_preferred_target(self.vm.as_raw_fd(), &mut pref_target)
861 .map_err(Error::CreateVCpu)?
862 };
863
864 kvi.target = pref_target.target;
865 unsafe { ioctl::kvm_arm_vcpu_init(vcpu.as_raw_fd(), &kvi).map_err(Error::CreateVCpu)? };
867 }
868
869 let vp = Vp {
870 vcpu,
871 run_data: VpPtr {
872 ptr: ptr.cast(),
873 len: self.mmap_size,
874 },
875 thread: RwLock::new(None),
876 _phantom: PhantomData,
877 };
878 if self.vps.len() <= vcpu_idx as usize {
879 self.vps.resize_with(vcpu_idx as usize + 1, || None);
880 }
881 assert!(self.vps[vcpu_idx as usize].replace(vp).is_none());
882
883 Ok(())
884 }
885
886 pub fn vp(&self, index: u32) -> Processor<'_> {
887 Processor(self, index)
888 }
889
890 pub fn request_msi(&self, msi: &kvm_msi) -> Result<()> {
891 unsafe {
894 ioctl::kvm_signal_msi(self.vm.as_raw_fd(), msi).map_err(Error::SignalMsi)?;
895 }
896 Ok(())
897 }
898
899 pub unsafe fn set_user_memory_region(
908 &self,
909 slot: u32,
910 data: *mut u8,
911 size: usize,
912 addr: u64,
913 readonly: bool,
914 ) -> Result<()> {
915 let region = kvm_userspace_memory_region {
916 slot,
917 flags: if readonly { KVM_MEM_READONLY } else { 0 },
918 guest_phys_addr: addr,
919 memory_size: size as u64,
920 userspace_addr: data as usize as u64,
921 };
922 unsafe {
925 ioctl::kvm_set_user_memory_region(self.vm.as_raw_fd(), ®ion)
926 .map_err(Error::SetMemoryRegion)?;
927 }
928 Ok(())
929 }
930
931 pub unsafe fn set_user_memory_region2(
942 &self,
943 slot: u32,
944 data: *mut u8,
945 size: usize,
946 addr: u64,
947 readonly: bool,
948 guest_memfd: Option<(&File, u64)>,
949 ) -> Result<()> {
950 let (guest_memfd, guest_memfd_offset, guest_memfd_flag) = guest_memfd
951 .map(|(file, offset)| (file.as_raw_fd() as u32, offset, KVM_MEM_GUEST_MEMFD))
952 .unwrap_or((0, 0, 0));
953 let region = kvm_userspace_memory_region2 {
954 slot,
955 flags: if readonly { KVM_MEM_READONLY } else { 0 } | guest_memfd_flag,
956 guest_phys_addr: addr,
957 memory_size: size as u64,
958 userspace_addr: data as usize as u64,
959 guest_memfd_offset,
960 guest_memfd,
961 ..Default::default()
962 };
963 unsafe {
967 ioctl::kvm_set_user_memory_region2(self.vm.as_raw_fd(), ®ion)
968 .map_err(Error::SetMemoryRegion)?;
969 }
970 Ok(())
971 }
972
973 pub fn create_guest_memfd(&self, size: u64) -> Result<File> {
974 let mut guest_memfd = kvm_create_guest_memfd {
975 size,
976 ..Default::default()
977 };
978 let fd = unsafe {
980 ioctl::kvm_create_guest_memfd(self.vm.as_raw_fd(), &mut guest_memfd)
981 .map_err(Error::CreateGuestMemfd)?
982 };
983 Ok(unsafe { File::from_raw_fd(fd) })
985 }
986
987 pub fn set_memory_attributes(&self, addr: u64, size: u64, attributes: u64) -> Result<()> {
988 let attr = kvm_memory_attributes {
989 address: addr,
990 size,
991 attributes,
992 ..Default::default()
993 };
994 unsafe {
996 ioctl::kvm_set_memory_attributes(self.vm.as_raw_fd(), &attr)
997 .map_err(Error::SetMemoryAttributes)?;
998 }
999 Ok(())
1000 }
1001
1002 pub fn set_gsi_routes(&self, routes: &[(u32, RoutingEntry)]) -> Result<()> {
1003 const MAX_ROUTES: usize = 2048;
1004 assert!(routes.len() <= MAX_ROUTES);
1005
1006 #[repr(C)]
1007 struct Routes {
1008 header: kvm_irq_routing,
1009 entries: [kvm_irq_routing_entry; MAX_ROUTES],
1010 }
1011
1012 let mut kvm_routes = Routes {
1013 header: Default::default(),
1014 entries: [Default::default(); MAX_ROUTES],
1015 };
1016 for (i, route) in routes.iter().enumerate() {
1017 let (type_, flags, u) = match route.1 {
1018 RoutingEntry::Msi {
1019 address_lo,
1020 address_hi,
1021 data,
1022 devid,
1023 } => {
1024 let (flags, anon) = if let Some(devid) = devid {
1025 (
1026 KVM_MSI_VALID_DEVID,
1027 kvm_irq_routing_msi__bindgen_ty_1 { devid },
1028 )
1029 } else {
1030 (0, Default::default())
1031 };
1032 (
1033 KVM_IRQ_ROUTING_MSI,
1034 flags,
1035 kvm_irq_routing_entry__bindgen_ty_1 {
1036 msi: kvm_irq_routing_msi {
1037 address_lo,
1038 address_hi,
1039 data,
1040 __bindgen_anon_1: anon,
1041 },
1042 },
1043 )
1044 }
1045 RoutingEntry::HvSint { vp, sint } => (
1046 KVM_IRQ_ROUTING_HV_SINT,
1047 0,
1048 kvm_irq_routing_entry__bindgen_ty_1 {
1049 hv_sint: kvm_irq_routing_hv_sint {
1050 vcpu: vp,
1051 sint: sint.into(),
1052 },
1053 },
1054 ),
1055 RoutingEntry::Irqchip { pin } => (
1056 KVM_IRQ_ROUTING_IRQCHIP,
1057 0,
1058 kvm_irq_routing_entry__bindgen_ty_1 {
1059 irqchip: kvm_irq_routing_irqchip { pin, irqchip: 0 },
1060 },
1061 ),
1062 };
1063 kvm_routes.entries[i] = kvm_irq_routing_entry {
1064 gsi: route.0,
1065 type_,
1066 flags,
1067 pad: 0,
1068 u,
1069 };
1070 kvm_routes.header.nr += 1;
1071 }
1072
1073 unsafe {
1076 ioctl::kvm_set_gsi_routing(self.vm.as_raw_fd(), &kvm_routes.header)
1077 .map_err(Error::SetGsiRouting)?;
1078 }
1079 Ok(())
1080 }
1081
1082 pub fn irqfd(&self, gsi: u32, event: RawFd, assign: bool) -> Result<()> {
1083 unsafe {
1086 ioctl::kvm_irqfd(
1087 self.vm.as_raw_fd(),
1088 &kvm_irqfd {
1089 fd: event as u32,
1090 gsi,
1091 flags: if assign { 0 } else { KVM_IRQFD_FLAG_DEASSIGN },
1092 resamplefd: 0,
1093 pad: [0; 16],
1094 },
1095 )
1096 .map_err(Error::IrqFd)
1097 .map(drop)
1098 }
1099 }
1100
1101 pub fn irq_line(&self, gsi: u32, level: bool) -> Result<()> {
1102 unsafe {
1105 ioctl::kvm_irq_line(
1106 self.vm.as_raw_fd(),
1107 &kvm_irq_level {
1108 __bindgen_anon_1: kvm_irq_level__bindgen_ty_1 { irq: gsi },
1109 level: level.into(),
1110 },
1111 )
1112 .map_err(Error::IrqLine)?;
1113 }
1114 Ok(())
1115 }
1116
1117 pub fn ioeventfd(
1118 &self,
1119 datamatch: u64,
1120 addr: u64,
1121 len: u32,
1122 fd: i32,
1123 flags: u32,
1124 ) -> Result<()> {
1125 unsafe {
1128 ioctl::kvm_ioeventfd(
1129 self.vm.as_raw_fd(),
1130 &kvm_ioeventfd {
1131 datamatch,
1132 addr,
1133 len,
1134 fd,
1135 flags,
1136 ..Default::default()
1137 },
1138 )
1139 .map_err(Error::IoEventFd)?;
1140 };
1141 Ok(())
1142 }
1143
1144 pub fn create_device(&self, ty: u32, flags: u32) -> nix::Result<Device> {
1145 unsafe {
1150 let mut device = kvm_create_device {
1151 type_: ty,
1152 fd: 0,
1153 flags,
1154 };
1155 ioctl::kvm_create_device(self.vm.as_raw_fd(), &mut device)?;
1156 Ok(Device(File::from_raw_fd(device.fd as i32)))
1157 }
1158 }
1159
1160 pub fn test_create_device(&self, ty: u32) -> nix::Result<()> {
1165 unsafe {
1169 let mut device = kvm_create_device {
1170 type_: ty,
1171 fd: 0,
1172 flags: KVM_CREATE_DEVICE_TEST,
1173 };
1174 ioctl::kvm_create_device(self.vm.as_raw_fd(), &mut device)?;
1175 }
1176 Ok(())
1177 }
1178
1179 pub fn get_clock_ns(&self) -> Result<kvm_clock_data> {
1181 let mut clock = kvm_clock_data::default();
1182 unsafe {
1184 ioctl::kvm_get_clock(self.vm.as_raw_fd(), &mut clock).map_err(Error::GetClock)?;
1185 }
1186 Ok(clock)
1187 }
1188
1189 pub fn set_clock_ns(&self, clock_ns: u64) -> Result<()> {
1191 let clock = kvm_clock_data {
1192 clock: clock_ns,
1193 ..Default::default()
1194 };
1195 unsafe {
1197 ioctl::kvm_set_clock(self.vm.as_raw_fd(), &clock).map_err(Error::SetClock)?;
1198 }
1199 Ok(())
1200 }
1201}
1202
1203pub struct Device(File);
1205
1206impl Device {
1207 pub unsafe fn set_device_attr<T>(
1212 &self,
1213 group: u32,
1214 attr: u32,
1215 addr: &T,
1216 flags: u32,
1217 ) -> nix::Result<()> {
1218 unsafe {
1220 ioctl::kvm_set_device_attr(
1221 self.0.as_raw_fd(),
1222 &kvm_device_attr {
1223 group,
1224 attr: attr as u64,
1225 addr: std::ptr::from_ref(addr) as u64,
1226 flags,
1227 },
1228 )?;
1229 }
1230 Ok(())
1231 }
1232}
1233
1234#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1235pub enum RoutingEntry {
1236 Irqchip {
1237 pin: u32,
1238 },
1239 Msi {
1240 address_lo: u32,
1241 address_hi: u32,
1242 data: u32,
1243 devid: Option<u32>,
1244 },
1245 HvSint {
1246 vp: u32,
1247 sint: u8,
1248 },
1249}
1250
1251pub struct Processor<'a>(&'a Partition, u32);
1252
1253impl<'a> Processor<'a> {
1254 pub fn enable_synic(&self) -> Result<()> {
1255 unsafe {
1259 ioctl::kvm_enable_cap(
1260 self.get().vcpu.as_raw_fd(),
1261 &kvm_enable_cap {
1262 cap: KVM_CAP_HYPERV_SYNIC2,
1263 ..Default::default()
1264 },
1265 )
1266 .map_err(|err| Error::EnableCap("hyperv_synic2", err))?;
1267 }
1268 Ok(())
1269 }
1270
1271 #[cfg(target_arch = "x86_64")]
1272 pub fn set_cpuid(&self, entries: &[kvm_cpuid_entry2]) -> Result<()> {
1273 const MAX_CPUID_ENTRIES: usize = 256;
1274 assert!(entries.len() <= MAX_CPUID_ENTRIES);
1275
1276 let mut cpuid: Cpuid = Cpuid {
1277 cpuid: Default::default(),
1278 entries: [Default::default(); MAX_CPUID_ENTRIES],
1279 };
1280 for (i, e) in entries.iter().enumerate() {
1281 cpuid.entries[i] = *e;
1282 cpuid.cpuid.nent += 1;
1283 }
1284
1285 unsafe {
1287 ioctl::kvm_set_cpuid2(self.get().vcpu.as_raw_fd(), &cpuid.cpuid)
1288 .map_err(Error::SetCpuid)?;
1289 }
1290 Ok(())
1291 }
1292
1293 fn get(&self) -> &'a Vp {
1294 self.0.vps[self.1 as usize].as_ref().expect("vp exists")
1295 }
1296
1297 pub fn force_exit(&self) {
1302 let vp = self.get();
1303 let thread = vp.thread.read();
1304 if let Some(thread) = *thread {
1305 if thread != Pthread::current() {
1306 thread
1307 .signal(libc::SIGRTMIN())
1308 .expect("thread cancel signal failed");
1309 }
1310 }
1311 }
1312
1313 pub fn interrupt(&self, vector: u32) -> Result<()> {
1314 unsafe {
1316 ioctl::kvm_interrupt(self.get().vcpu.as_raw_fd(), &kvm_interrupt { irq: vector })
1317 .map_err(Error::Interrupt)?;
1318 };
1319 Ok(())
1320 }
1321
1322 pub fn set_reg64(&self, reg_id: u64, value: u64) -> Result<()> {
1324 let reg = kvm_one_reg {
1325 id: reg_id,
1326 addr: std::ptr::from_ref(&value) as u64,
1327 };
1328 unsafe {
1330 ioctl::kvm_set_reg(self.get().vcpu.as_raw_fd(), ®).map_err(Error::SetRegs)?;
1331 }
1332 Ok(())
1333 }
1334
1335 #[cfg(not(target_arch = "aarch64"))]
1336 pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> {
1337 unsafe {
1340 ioctl::kvm_set_regs(self.get().vcpu.as_raw_fd(), regs).map_err(Error::SetRegs)?;
1341 }
1342 Ok(())
1343 }
1344
1345 #[cfg(not(target_arch = "aarch64"))]
1346 pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> {
1347 unsafe {
1350 ioctl::kvm_set_sregs(self.get().vcpu.as_raw_fd(), sregs).map_err(Error::SetRegs)?;
1351 }
1352 Ok(())
1353 }
1354
1355 pub fn get_reg64(&self, reg_id: u64) -> Result<u64> {
1357 let mut value: u64 = 0;
1358 let reg = kvm_one_reg {
1359 id: reg_id,
1360 addr: std::ptr::from_mut(&mut value) as u64,
1361 };
1362 unsafe {
1364 ioctl::kvm_get_reg(self.get().vcpu.as_raw_fd(), ®).map_err(Error::GetRegs)?;
1365 }
1366
1367 Ok(value)
1368 }
1369
1370 #[cfg(not(target_arch = "aarch64"))]
1371 pub fn get_regs(&self) -> Result<kvm_regs> {
1372 let mut regs = Default::default();
1373 unsafe {
1376 ioctl::kvm_get_regs(self.get().vcpu.as_raw_fd(), &mut regs).map_err(Error::GetRegs)?;
1377 }
1378 Ok(regs)
1379 }
1380
1381 #[cfg(not(target_arch = "aarch64"))]
1382 pub fn get_sregs(&self) -> Result<kvm_sregs> {
1383 let mut sregs = Default::default();
1384 unsafe {
1387 ioctl::kvm_get_sregs(self.get().vcpu.as_raw_fd(), &mut sregs)
1388 .map_err(Error::GetSRegs)?;
1389 }
1390 Ok(sregs)
1391 }
1392
1393 #[cfg(target_arch = "x86_64")]
1394 pub fn get_msrs(&self, msrs: &[u32], values: &mut [u64]) -> Result<()> {
1395 const MAX_MSR_ENTRIES: usize = 256;
1396 assert_eq!(msrs.len(), values.len());
1397 assert!(msrs.len() <= MAX_MSR_ENTRIES);
1398
1399 #[repr(C)]
1400 struct Msrs {
1401 header: kvm_msrs,
1402 entries: [kvm_msr_entry; MAX_MSR_ENTRIES],
1403 }
1404 let mut input = Msrs {
1405 header: kvm_msrs {
1406 nmsrs: msrs.len() as u32,
1407 ..Default::default()
1408 },
1409 entries: [Default::default(); MAX_MSR_ENTRIES],
1410 };
1411 for (i, msr) in msrs.iter().enumerate() {
1412 input.entries[i] = kvm_msr_entry {
1413 index: *msr,
1414 reserved: 0,
1415 data: 0,
1416 };
1417 }
1418
1419 let completed = unsafe {
1421 ioctl::kvm_get_msrs(self.get().vcpu.as_raw_fd(), &mut input.header)
1422 .map_err(Error::GetMsrs)?
1423 } as usize;
1424 assert!(completed <= msrs.len());
1425 if completed < msrs.len() {
1426 return Err(Error::IncompleteMsrs {
1427 requested: msrs.len(),
1428 completed,
1429 failed_msr: msrs.get(completed).copied().unwrap(),
1430 write: false,
1431 });
1432 }
1433 for (v, e) in values.iter_mut().zip(&input.entries) {
1434 *v = e.data;
1435 }
1436 Ok(())
1437 }
1438
1439 #[cfg(target_arch = "x86_64")]
1440 pub fn set_msrs(&self, msrs: &[(u32, u64)]) -> Result<()> {
1441 const MAX_MSR_ENTRIES: usize = 256;
1442 assert!(msrs.len() <= MAX_MSR_ENTRIES);
1443
1444 #[repr(C)]
1445 struct Msrs {
1446 header: kvm_msrs,
1447 entries: [kvm_msr_entry; MAX_MSR_ENTRIES],
1448 }
1449 let mut input = Msrs {
1450 header: kvm_msrs {
1451 nmsrs: msrs.len() as u32,
1452 ..Default::default()
1453 },
1454 entries: [Default::default(); MAX_MSR_ENTRIES],
1455 };
1456 for (i, msr) in msrs.iter().enumerate() {
1457 input.entries[i] = kvm_msr_entry {
1458 index: msr.0,
1459 reserved: 0,
1460 data: msr.1,
1461 };
1462 }
1463
1464 let completed = unsafe {
1466 ioctl::kvm_set_msrs(self.get().vcpu.as_raw_fd(), &input.header)
1467 .map_err(Error::SetMsrs)?
1468 } as usize;
1469 assert!(completed <= msrs.len());
1470 if completed < msrs.len() {
1471 return Err(Error::IncompleteMsrs {
1472 requested: msrs.len(),
1473 completed,
1474 failed_msr: msrs.get(completed).copied().unwrap().0,
1475 write: true,
1476 });
1477 }
1478 Ok(())
1479 }
1480
1481 #[cfg(target_arch = "x86_64")]
1488 pub fn setup_mce(&self, mcg_cap: u64) -> Result<()> {
1489 unsafe {
1491 ioctl::kvm_x86_setup_mce(self.get().vcpu.as_raw_fd(), &mcg_cap)
1492 .map_err(Error::SetupMce)?;
1493 }
1494 Ok(())
1495 }
1496
1497 #[cfg(target_arch = "x86_64")]
1498 pub fn get_lapic(&self, state: &mut [u8; 1024]) -> Result<()> {
1499 assert_eq!(size_of_val(state), size_of::<kvm_lapic_state>());
1500
1501 unsafe {
1504 ioctl::kvm_get_lapic(self.get().vcpu.as_raw_fd(), state.as_mut_ptr().cast())
1505 .map_err(Error::GetLApic)?;
1506 }
1507 Ok(())
1508 }
1509
1510 #[cfg(target_arch = "x86_64")]
1511 pub fn set_lapic(&self, state: &[u8; 1024]) -> Result<()> {
1512 assert_eq!(size_of_val(state), size_of::<kvm_lapic_state>());
1513
1514 unsafe {
1517 ioctl::kvm_set_lapic(self.get().vcpu.as_raw_fd(), state.as_ptr().cast())
1518 .map_err(Error::SetLApic)?;
1519 }
1520 Ok(())
1521 }
1522
1523 #[cfg(target_arch = "x86_64")]
1524 pub fn get_xsave(&self, state: &mut [u8; 4096]) -> Result<()> {
1525 assert_eq!(size_of_val(state), size_of::<kvm_xsave>());
1526
1527 unsafe {
1530 ioctl::kvm_get_xsave(self.get().vcpu.as_raw_fd(), state.as_mut_ptr().cast())
1531 .map_err(Error::GetXsave)?;
1532 }
1533 Ok(())
1534 }
1535
1536 #[cfg(target_arch = "x86_64")]
1537 pub fn set_xsave(&self, state: &[u8; 4096]) -> Result<()> {
1538 assert_eq!(size_of_val(state), size_of::<kvm_xsave>());
1539
1540 unsafe {
1543 ioctl::kvm_set_xsave(self.get().vcpu.as_raw_fd(), state.as_ptr().cast())
1544 .map_err(Error::SetXsave)?;
1545 }
1546 Ok(())
1547 }
1548
1549 #[cfg(target_arch = "x86_64")]
1550 pub fn set_debug_regs(&self, regs: &DebugRegisters) -> Result<()> {
1551 let data = kvm_debugregs {
1552 db: regs.db,
1553 dr6: regs.dr6,
1554 dr7: regs.dr7,
1555 flags: 0,
1556 reserved: [0; 9],
1557 };
1558
1559 unsafe {
1562 ioctl::kvm_set_debugregs(self.get().vcpu.as_raw_fd(), &data)
1563 .map_err(Error::SetDebugRegs)?;
1564 }
1565 Ok(())
1566 }
1567
1568 #[cfg(target_arch = "x86_64")]
1569 pub fn get_debug_regs(&self) -> Result<DebugRegisters> {
1570 let mut data = Default::default();
1571
1572 unsafe {
1575 ioctl::kvm_get_debugregs(self.get().vcpu.as_raw_fd(), &mut data)
1576 .map_err(Error::GetDebugRegs)?;
1577 }
1578
1579 Ok(DebugRegisters {
1580 db: data.db,
1581 dr6: data.dr6,
1582 dr7: data.dr7,
1583 })
1584 }
1585
1586 #[cfg(target_arch = "x86_64")]
1587 pub fn set_xcr0(&self, value: u64) -> Result<()> {
1588 let mut data = kvm_xcrs {
1589 nr_xcrs: 1,
1590 ..Default::default()
1591 };
1592 data.xcrs[0] = kvm_xcr {
1593 xcr: 0,
1594 reserved: 0,
1595 value,
1596 };
1597
1598 unsafe {
1601 ioctl::kvm_set_xcrs(self.get().vcpu.as_raw_fd(), &data).map_err(Error::GetXcrs)?;
1602 }
1603 Ok(())
1604 }
1605
1606 #[cfg(target_arch = "x86_64")]
1607 pub fn get_xcr0(&self) -> Result<u64> {
1608 let mut data = Default::default();
1609
1610 unsafe {
1613 ioctl::kvm_get_xcrs(self.get().vcpu.as_raw_fd(), &mut data).map_err(Error::SetXcrs)?;
1614 }
1615
1616 if data.nr_xcrs < 1 {
1617 return Err(Error::XsaveNotEnabled);
1618 }
1619 assert_eq!(data.nr_xcrs, 1);
1620 assert_eq!(data.xcrs[0].xcr, 0);
1621 Ok(data.xcrs[0].value)
1622 }
1623
1624 pub fn set_mp_state(&self, state: u32) -> Result<()> {
1625 let state = kvm_mp_state { mp_state: state };
1626 unsafe {
1629 ioctl::kvm_set_mp_state(self.get().vcpu.as_raw_fd(), &state)
1630 .map_err(Error::SetMpState)?;
1631 }
1632 Ok(())
1633 }
1634
1635 pub fn get_mp_state(&self) -> Result<u32> {
1636 let mut state = Default::default();
1637 unsafe {
1640 ioctl::kvm_get_mp_state(self.get().vcpu.as_raw_fd(), &mut state)
1641 .map_err(Error::GetMpState)?;
1642 }
1643 Ok(state.mp_state)
1644 }
1645
1646 pub fn set_vcpu_events(&self, events: &kvm_vcpu_events) -> Result<()> {
1647 unsafe {
1650 ioctl::kvm_set_vcpu_events(self.get().vcpu.as_raw_fd(), events)
1651 .map_err(Error::SetVcpuEvents)?;
1652 }
1653 Ok(())
1654 }
1655
1656 pub fn get_vcpu_events(&self) -> Result<kvm_vcpu_events> {
1657 let mut events = Default::default();
1658 unsafe {
1661 ioctl::kvm_get_vcpu_events(self.get().vcpu.as_raw_fd(), &mut events)
1662 .map_err(Error::GetVcpuEvents)?;
1663 }
1664 Ok(events)
1665 }
1666
1667 pub fn translate_gva(&self, gva: u64) -> Result<kvm_translation> {
1668 let mut translation = kvm_translation {
1669 linear_address: gva,
1670 ..Default::default()
1671 };
1672
1673 unsafe {
1675 ioctl::kvm_translation(self.get().vcpu.as_raw_fd(), &mut translation)
1676 .map_err(Error::TranslateGva)?;
1677 }
1678
1679 Ok(translation)
1680 }
1681
1682 #[cfg(target_arch = "x86_64")]
1685 pub fn set_guest_debug(&self, control: u32, db: [u64; 4], dr7: u64) -> Result<()> {
1686 let debug = kvm_guest_debug {
1688 control,
1689 pad: 0,
1690 arch: kvm_guest_debug_arch {
1691 debugreg: [db[0], db[1], db[2], db[3], 0, 0, 0, dr7],
1692 },
1693 };
1694
1695 unsafe {
1698 ioctl::kvm_set_guest_debug(self.get().vcpu.as_raw_fd(), &debug)
1699 .map_err(Error::GetRegs)?;
1700 }
1701 Ok(())
1702 }
1703
1704 pub unsafe fn set_device_attr<T>(
1709 &self,
1710 group: u32,
1711 attr: u32,
1712 addr: &T,
1713 flags: u32,
1714 ) -> nix::Result<libc::c_int> {
1715 unsafe {
1717 ioctl::kvm_set_device_attr(
1718 self.get().vcpu.as_raw_fd(),
1719 &kvm_device_attr {
1720 group,
1721 attr: attr as u64,
1722 addr: std::ptr::from_ref(addr) as u64,
1723 flags,
1724 },
1725 )
1726 }
1727 }
1728
1729 pub fn runner(&self) -> VpRunner<'a> {
1730 assert!(
1733 self.get()
1734 .thread
1735 .write()
1736 .replace(Pthread::current())
1737 .is_none()
1738 );
1739
1740 VpRunner {
1741 partition: self.0,
1742 idx: self.1,
1743 _not_send_sync: PhantomData,
1744 }
1745 }
1746}
1747
1748pub struct VpRunner<'a> {
1749 partition: &'a Partition,
1750 idx: u32,
1751 _not_send_sync: PhantomData<*const u8>,
1754}
1755
1756impl Drop for VpRunner<'_> {
1757 fn drop(&mut self) {
1758 let thread = self.get().thread.write().take();
1760 assert_eq!(thread, Some(Pthread::current()));
1761 }
1762}
1763
1764impl<'a> VpRunner<'a> {
1765 fn get(&self) -> &'a Vp {
1766 self.partition.vp(self.idx).get()
1767 }
1768
1769 fn run_data(&mut self) -> &mut kvm_run {
1770 let vp = self.get();
1771 unsafe { &mut *vp.run_data.ptr }
1776 }
1777
1778 #[cfg_attr(target_arch = "aarch64", expect(dead_code))]
1779 fn run_data_slice(&mut self) -> &mut [u8] {
1780 let vp = self.get();
1781 unsafe { std::slice::from_raw_parts_mut(vp.run_data.ptr.cast::<u8>(), vp.run_data.len) }
1786 }
1787
1788 fn run_vp_once(&mut self) -> Result<bool> {
1790 CURRENT_KVM_RUN.with(|r| {
1791 let vp = self.get();
1792
1793 self.run_data().immediate_exit = 0;
1796
1797 match r.swap(vp.run_data.ptr as usize, Ordering::Relaxed) {
1800 NO_KVM_RUN => {}
1801 CANCEL_KVM_RUN => {
1802 unsafe { set_immediate_exit(vp.run_data.ptr) };
1813 }
1814 state => unreachable!("unexpected state {:#x}", state),
1815 }
1816
1817 let result = unsafe { ioctl::kvm_run(vp.vcpu.as_raw_fd(), 0) };
1819 CURRENT_KVM_RUN.with(|r| r.store(NO_KVM_RUN, Ordering::Relaxed));
1820 match result {
1821 Ok(_) => Ok(true),
1822 Err(err) => match err {
1823 nix::errno::Errno::EINTR | nix::errno::Errno::EAGAIN => Ok(false),
1824 _ if self.run_data().exit_reason == KVM_EXIT_MEMORY_FAULT => {
1825 let memory_fault = unsafe { self.run_data().__bindgen_anon_1.memory_fault };
1827 Err(Error::RunMemoryFault {
1828 flags: memory_fault.flags,
1829 gpa: memory_fault.gpa,
1830 size: memory_fault.size,
1831 source: err,
1832 })
1833 }
1834 _ => Err(Error::Run(err)),
1835 },
1836 }
1837 })
1838 }
1839
1840 pub fn complete_exit(&mut self) -> Result<Exit<'_>, Error> {
1844 CURRENT_KVM_RUN.with(|run| run.store(CANCEL_KVM_RUN, Ordering::Relaxed));
1845 self.run()
1846 }
1847
1848 pub fn run(&mut self) -> Result<Exit<'_>, Error> {
1853 if !self.run_vp_once()? {
1854 return Ok(Exit::Interrupted);
1855 }
1856
1857 let exit = match self.run_data().exit_reason {
1858 #[cfg(target_arch = "x86_64")]
1859 KVM_EXIT_DEBUG => {
1860 let debug = unsafe { &self.run_data().__bindgen_anon_1.debug };
1862
1863 Exit::Debug {
1864 exception: debug.arch.exception,
1865 pc: debug.arch.pc,
1866 dr6: debug.arch.dr6,
1867 dr7: debug.arch.dr7,
1868 }
1869 }
1870 #[cfg(target_arch = "x86_64")]
1871 KVM_EXIT_IO => {
1872 let io = unsafe { self.run_data().__bindgen_anon_1.io };
1874
1875 let offset = io.data_offset as usize;
1876 let data = &mut self.run_data_slice()
1877 [offset..offset + io.size as usize * io.count as usize];
1878 if io.direction == KVM_EXIT_IO_IN as u8 {
1879 Exit::IoIn {
1880 port: io.port,
1881 size: io.size,
1882 data,
1883 }
1884 } else {
1885 Exit::IoOut {
1886 port: io.port,
1887 size: io.size,
1888 data,
1889 }
1890 }
1891 }
1892 #[cfg(target_arch = "x86_64")]
1893 KVM_EXIT_IRQ_WINDOW_OPEN => {
1894 let rdata = self.run_data();
1895 assert!(rdata.ready_for_interrupt_injection != 0);
1896 rdata.request_interrupt_window = 0;
1897 Exit::InterruptWindow
1898 }
1899 KVM_EXIT_MMIO => {
1900 let mmio = unsafe { &mut self.run_data().__bindgen_anon_1.mmio };
1902 if mmio.is_write != 0 {
1903 Exit::MmioWrite {
1904 address: mmio.phys_addr,
1905 data: &mmio.data[0..mmio.len as usize],
1906 }
1907 } else {
1908 mmio.data = [0; 8];
1909 Exit::MmioRead {
1910 address: mmio.phys_addr,
1911 data: &mut mmio.data[0..mmio.len as usize],
1912 }
1913 }
1914 }
1915 KVM_EXIT_SHUTDOWN => Exit::Shutdown,
1916 #[cfg(target_arch = "x86_64")]
1917 KVM_EXIT_HYPERV => {
1918 let hyperv = unsafe { &mut self.run_data().__bindgen_anon_1.hyperv };
1920 match hyperv.type_ {
1921 KVM_EXIT_HYPERV_HCALL => {
1922 let hcall = unsafe { &mut hyperv.u.hcall };
1924 Exit::HvHypercall {
1925 input: hcall.input,
1926 result: &mut hcall.result,
1927 params: hcall.params,
1928 }
1929 }
1930 KVM_EXIT_HYPERV_SYNIC => {
1931 let synic = unsafe { &hyperv.u.synic };
1933 Exit::SynicUpdate {
1934 msr: synic.msr,
1935 control: synic.control,
1936 siefp: synic.evt_page,
1937 simp: synic.msg_page,
1938 }
1939 }
1940 _ => return Err(Error::UnknownHvExit(hyperv.type_)),
1941 }
1942 }
1943 #[cfg(target_arch = "x86_64")]
1944 KVM_EXIT_IOAPIC_EOI => {
1945 let eoi = unsafe { &mut self.run_data().__bindgen_anon_1.eoi };
1947
1948 Exit::Eoi { irq: eoi.vector }
1949 }
1950 KVM_EXIT_FAIL_ENTRY => {
1951 let fail_entry = unsafe { &self.run_data().__bindgen_anon_1.fail_entry };
1953 Exit::FailEntry {
1954 hardware_entry_failure_reason: fail_entry.hardware_entry_failure_reason,
1955 }
1956 }
1957 KVM_EXIT_INTERNAL_ERROR => {
1958 let internal = unsafe { &self.run_data().__bindgen_anon_1.internal };
1960 if internal.suberror == KVM_INTERNAL_ERROR_EMULATION {
1961 Exit::EmulationFailure {
1963 instruction_bytes: &[],
1964 }
1965 } else {
1966 Exit::InternalError {
1967 error: internal.suberror,
1968 data: &internal.data[..internal.ndata as usize],
1969 }
1970 }
1971 }
1972 #[cfg(target_arch = "x86_64")]
1973 KVM_EXIT_HYPERCALL => {
1974 let hypercall = unsafe { &mut self.run_data().__bindgen_anon_1.hypercall };
1976 Exit::Hypercall {
1977 nr: hypercall.nr,
1978 args: hypercall.args,
1979 result: &mut hypercall.ret,
1980 flags: unsafe { hypercall.__bindgen_anon_1.flags },
1982 }
1983 }
1984 #[cfg(target_arch = "x86_64")]
1985 KVM_EXIT_X86_WRMSR => {
1986 let msr = unsafe { &mut self.run_data().__bindgen_anon_1.msr };
1988 msr.error = 0;
1989 Exit::MsrWrite {
1990 index: msr.index,
1991 data: msr.data,
1992 error: &mut msr.error,
1993 }
1994 }
1995 #[cfg(target_arch = "x86_64")]
1996 KVM_EXIT_X86_RDMSR => {
1997 let msr = unsafe { &mut self.run_data().__bindgen_anon_1.msr };
1999 msr.data = 0;
2000 msr.error = 0;
2001 Exit::MsrRead {
2002 index: msr.index,
2003 data: &mut msr.data,
2004 error: &mut msr.error,
2005 }
2006 }
2007 KVM_EXIT_SYSTEM_EVENT => {
2008 let system_event = unsafe { &self.run_data().__bindgen_anon_1.system_event };
2010 Exit::SystemEvent {
2011 event_type: system_event.type_,
2012 event_flags: unsafe { system_event.__bindgen_anon_1.flags },
2014 }
2015 }
2016 exit_reason => return Err(Error::UnknownExit(exit_reason)),
2017 };
2018 Ok(exit)
2019 }
2020
2021 #[must_use]
2026 pub fn check_or_request_interrupt_window(&mut self) -> bool {
2027 let rdata = self.run_data();
2028 if rdata.ready_for_interrupt_injection != 0 {
2029 true
2030 } else {
2031 rdata.request_interrupt_window = 1;
2032 false
2033 }
2034 }
2035
2036 pub fn inject_extint_interrupt(&mut self, vector: u8) -> Result<()> {
2042 self.partition.vp(self.idx).interrupt(vector.into())?;
2043 self.run_data().ready_for_interrupt_injection = 0;
2046 Ok(())
2047 }
2048}
2049
2050#[derive(Debug)]
2051pub enum Exit<'a> {
2052 Interrupted,
2053 #[cfg(target_arch = "x86_64")]
2054 InterruptWindow,
2055 #[cfg(target_arch = "x86_64")]
2056 IoIn {
2057 port: u16,
2058 size: u8,
2059 data: &'a mut [u8],
2060 },
2061 #[cfg(target_arch = "x86_64")]
2062 IoOut {
2063 port: u16,
2064 size: u8,
2065 data: &'a [u8],
2066 },
2067 MmioRead {
2068 address: u64,
2069 data: &'a mut [u8],
2070 },
2071 MmioWrite {
2072 address: u64,
2073 data: &'a [u8],
2074 },
2075 #[cfg(target_arch = "x86_64")]
2076 Hypercall {
2077 nr: u64,
2078 args: [u64; 6],
2079 result: &'a mut u64,
2080 flags: u64,
2081 },
2082 #[cfg(target_arch = "x86_64")]
2083 MsrRead {
2084 index: u32,
2085 data: &'a mut u64,
2086 error: &'a mut u8,
2087 },
2088 #[cfg(target_arch = "x86_64")]
2089 MsrWrite {
2090 index: u32,
2091 data: u64,
2092 error: &'a mut u8,
2093 },
2094 Shutdown,
2095 FailEntry {
2096 hardware_entry_failure_reason: u64,
2097 },
2098 InternalError {
2099 error: u32,
2100 data: &'a [u64],
2101 },
2102 EmulationFailure {
2103 instruction_bytes: &'a [u8],
2104 },
2105 #[cfg(target_arch = "x86_64")]
2106 SynicUpdate {
2107 msr: u32,
2108 control: u64,
2109 siefp: u64,
2110 simp: u64,
2111 },
2112 #[cfg(target_arch = "x86_64")]
2113 HvHypercall {
2114 input: u64,
2115 result: &'a mut u64,
2116 params: [u64; 2],
2117 },
2118 #[cfg(target_arch = "x86_64")]
2119 Debug {
2120 exception: u32,
2121 pc: u64,
2122 dr6: u64,
2123 dr7: u64,
2124 },
2125 #[cfg(target_arch = "x86_64")]
2126 Eoi {
2127 irq: u8,
2128 },
2129 SystemEvent {
2130 event_type: u32,
2131 event_flags: u64,
2132 },
2133}
2134
2135pub fn init() {
2137 static SIGNAL_HANDLER_INIT: Once = Once::new();
2138 SIGNAL_HANDLER_INIT.call_once(|| {
2139 let handler = || {
2140 CURRENT_KVM_RUN.with(|run| {
2141 let rdata = run.load(Ordering::Relaxed);
2144 match rdata {
2145 NO_KVM_RUN => run.store(CANCEL_KVM_RUN, Ordering::Relaxed),
2146 CANCEL_KVM_RUN => {}
2147 _ => {
2148 unsafe { set_immediate_exit(rdata as *mut kvm_run) };
2151 }
2152 }
2153 })
2154 };
2155 CURRENT_KVM_RUN.with(|value| {
2157 std::hint::black_box(value);
2158 });
2159 unsafe {
2164 signal_hook::low_level::register(libc::SIGRTMIN(), handler).unwrap();
2165 }
2166 });
2167}
2168
2169const NO_KVM_RUN: usize = 0;
2170const CANCEL_KVM_RUN: usize = 1;
2171
2172thread_local! {
2173 static CURRENT_KVM_RUN: AtomicUsize = const { AtomicUsize::new(NO_KVM_RUN) };
2174}
2175
2176#[expect(clippy::missing_safety_doc)]
2181unsafe fn set_immediate_exit(rdata: *mut kvm_run) {
2182 unsafe {
2187 (*(std::ptr::addr_of!((*rdata).immediate_exit).cast::<AtomicU8>()))
2188 .store(1, Ordering::Relaxed);
2189 }
2190}
2191
2192pub struct DebugRegisters {
2193 pub db: [u64; 4],
2195 pub dr6: u64,
2196 pub dr7: u64,
2197}
2198
2199#[cfg(all(test, target_arch = "x86_64"))]
2200mod tests {
2201 use super::*;
2202
2203 #[test]
2204 fn sev_snp_page_type_values_match_kvm_uapi() {
2205 assert_eq!(SevSnpPageType::Normal.as_uapi(), 1);
2206 assert_eq!(SevSnpPageType::Zero.as_uapi(), 3);
2207 assert_eq!(SevSnpPageType::Unmeasured.as_uapi(), 4);
2208 assert_eq!(SevSnpPageType::Secrets.as_uapi(), 5);
2209 assert_eq!(SevSnpPageType::Cpuid.as_uapi(), 6);
2210 }
2211
2212 #[test]
2213 fn sev_snp_launch_update_uses_expected_zero_page_shape() {
2214 let update = kvm_sev_snp_launch_update {
2215 gfn_start: 0x1234,
2216 uaddr: 0,
2217 len: 0x2000,
2218 type_: SevSnpPageType::Zero.as_uapi(),
2219 ..Default::default()
2220 };
2221
2222 assert_eq!(update.gfn_start, 0x1234);
2223 assert_eq!(update.uaddr, 0);
2224 assert_eq!(update.len, 0x2000);
2225 assert_eq!(update.type_, KVM_SEV_SNP_PAGE_TYPE_ZERO_UAPI);
2226 assert_eq!(update.flags, 0);
2227 }
2228}