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 #[cfg(target_arch = "x86_64")]
131 ioctl_write_ptr!(kvm_get_device_attr, KVMIO, 0xe2, kvm_device_attr);
132 ioctl_readwrite!(kvm_create_guest_memfd, KVMIO, 0xd4, kvm_create_guest_memfd);
133 #[cfg(target_arch = "aarch64")]
134 ioctl_readwrite_bad!(
135 kvm_arm_rmi_populate,
136 request_code_readwrite!(KVMIO, 0xd7, size_of::<KvmArmRmiPopulate>()),
137 KvmArmRmiPopulate
138 );
139 #[cfg(target_arch = "x86_64")]
140 ioctl_readwrite_bad!(
141 kvm_memory_encrypt_op,
142 request_code_readwrite!(KVMIO, 0xba, size_of::<libc::c_ulong>()),
143 kvm_sev_cmd
144 );
145 #[cfg(target_arch = "x86_64")]
146 pub unsafe fn kvm_memory_encrypt_op_supported(fd: libc::c_int) -> nix::Result<()> {
150 match unsafe {
153 libc::ioctl(
154 fd,
155 request_code_readwrite!(KVMIO, 0xba, size_of::<libc::c_ulong>()),
156 std::ptr::null_mut::<libc::c_void>(),
157 )
158 } {
159 0 => Ok(()),
160 _ => Err(Errno::last()),
161 }
162 }
163}
164
165#[cfg(target_arch = "x86_64")]
166const KVM_CAP_VM_TYPES_UAPI: u32 = 235;
167#[cfg(target_arch = "x86_64")]
168const KVM_CAP_EXIT_HYPERCALL_UAPI: u32 = 201;
169#[cfg(target_arch = "x86_64")]
170pub const KVM_HC_MAP_GPA_RANGE_UAPI: u64 = 12;
171#[cfg(target_arch = "x86_64")]
172pub const KVM_MAP_GPA_RANGE_ENCRYPTED_UAPI: u64 = 1 << 4;
173#[cfg(target_arch = "x86_64")]
174pub const KVM_MAP_GPA_RANGE_DECRYPTED_UAPI: u64 = 0 << 4;
175#[cfg(target_arch = "x86_64")]
176const KVM_X86_SNP_VM_UAPI: libc::c_int = 4;
177
178pub const KVM_MEMORY_EXIT_FLAG_PRIVATE_UAPI: u64 = 1 << 3;
179
180#[cfg(target_arch = "aarch64")]
181pub const KVM_CAP_ARM_RMI_UAPI: u32 = 249;
182#[cfg(target_arch = "aarch64")]
183pub const KVM_ARM_RMI_POPULATE_FLAGS_MEASURE_UAPI: u32 = 1 << 0;
184#[cfg(target_arch = "aarch64")]
185const KVM_VM_TYPE_ARM_IPA_SIZE_MASK_UAPI: u64 = 0xff;
186#[cfg(target_arch = "aarch64")]
187const KVM_VM_TYPE_ARM_REALM_UAPI: u64 = 1 << 30;
188
189#[cfg(target_arch = "x86_64")]
190pub const KVM_SEV_SNP_PAGE_TYPE_NORMAL_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_NORMAL as u8;
191#[cfg(target_arch = "x86_64")]
192pub const KVM_SEV_SNP_PAGE_TYPE_ZERO_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_ZERO as u8;
193#[cfg(target_arch = "x86_64")]
194pub const KVM_SEV_SNP_PAGE_TYPE_UNMEASURED_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_UNMEASURED as u8;
195#[cfg(target_arch = "x86_64")]
196pub const KVM_SEV_SNP_PAGE_TYPE_SECRETS_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_SECRETS as u8;
197#[cfg(target_arch = "x86_64")]
198pub const KVM_SEV_SNP_PAGE_TYPE_CPUID_UAPI: u8 = KVM_SEV_SNP_PAGE_TYPE_CPUID as u8;
199
200#[derive(Debug, Copy, Clone, Eq, PartialEq)]
201pub enum VmType {
202 Default,
203 #[cfg(target_arch = "x86_64")]
204 Snp,
205 #[cfg(target_arch = "aarch64")]
206 Realm {
207 ipa_bits: u8,
208 },
209}
210
211#[cfg(target_arch = "x86_64")]
212#[derive(Debug, Copy, Clone, Eq, PartialEq)]
213pub enum SevSnpPageType {
214 Normal,
215 Zero,
216 Unmeasured,
217 Secrets,
218 Cpuid,
219}
220
221#[cfg(target_arch = "x86_64")]
222impl SevSnpPageType {
223 pub const fn as_uapi(self) -> u8 {
224 match self {
225 SevSnpPageType::Normal => KVM_SEV_SNP_PAGE_TYPE_NORMAL_UAPI,
226 SevSnpPageType::Zero => KVM_SEV_SNP_PAGE_TYPE_ZERO_UAPI,
227 SevSnpPageType::Unmeasured => KVM_SEV_SNP_PAGE_TYPE_UNMEASURED_UAPI,
228 SevSnpPageType::Secrets => KVM_SEV_SNP_PAGE_TYPE_SECRETS_UAPI,
229 SevSnpPageType::Cpuid => KVM_SEV_SNP_PAGE_TYPE_CPUID_UAPI,
230 }
231 }
232}
233
234#[cfg(target_arch = "aarch64")]
235#[repr(C)]
236#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
237pub struct KvmArmRmiPopulate {
238 pub base: u64,
239 pub size: u64,
240 pub source_uaddr: u64,
241 pub flags: u32,
242 pub reserved: u32,
243}
244
245#[derive(Error, Debug)]
246pub enum Error {
247 #[error("failed to open /dev/kvm")]
248 OpenKvm(#[source] io::Error),
249 #[error("SignalMsi")]
250 SignalMsi(#[source] nix::Error),
251 #[error("SetMemoryRegion")]
252 SetMemoryRegion(#[source] nix::Error),
253 #[error("SetMemoryAttributes")]
254 SetMemoryAttributes(#[source] nix::Error),
255 #[error("CreateGuestMemfd")]
256 CreateGuestMemfd(#[source] nix::Error),
257 #[error("CreateVm")]
258 CreateVm(#[source] nix::Error),
259 #[cfg(target_arch = "aarch64")]
260 #[error("ArmRmiPopulate")]
261 ArmRmiPopulate(#[source] nix::Error),
262 #[error("missing KVM capability: {0}")]
263 MissingCapability(&'static str),
264 #[error("unsupported KVM VM type: {0:?}")]
265 UnsupportedVmType(VmType),
266 #[cfg(target_arch = "x86_64")]
267 #[error("MemoryEncryptOp({command}, firmware_error={firmware_error:#x})")]
268 MemoryEncryptOp {
269 command: &'static str,
270 firmware_error: u32,
271 #[source]
272 source: nix::Error,
273 },
274 #[error("EnableCap({0})")]
275 EnableCap(&'static str, #[source] nix::Error),
276 #[error("CreateVCpu")]
277 CreateVCpu(#[source] nix::Error),
278 #[error("GetRegs")]
279 GetRegs(#[source] nix::Error),
280 #[error("GetSRegs")]
281 GetSRegs(#[source] nix::Error),
282 #[error("SetRegs")]
283 SetRegs(#[source] nix::Error),
284 #[error("SetSRegs")]
285 SetSRegs(#[source] nix::Error),
286 #[error("Run")]
287 Run(#[source] nix::Error),
288 #[error("RunMemoryFault(flags={flags:#x}, gpa={gpa:#x}, size={size:#x})")]
289 RunMemoryFault {
290 flags: u64,
291 gpa: u64,
292 size: u64,
293 #[source]
294 source: nix::Error,
295 },
296 #[error("GetVCpuMmapSize")]
297 GetVCpuMmapSize(#[source] nix::Error),
298 #[error("MmapVCpu")]
299 MmapVCpu(#[source] io::Error),
300 #[error("SetFpu")]
301 SetFpu(#[source] nix::Error),
302 #[error("GetSupportedCpuid")]
303 GetSupportedCpuid(#[source] nix::Error),
304 #[error("SetCpuid")]
305 SetCpuid(#[source] nix::Error),
306 #[error("Interrupt")]
307 Interrupt(#[source] nix::Error),
308 #[error("GetLApic")]
309 GetLApic(#[source] nix::Error),
310 #[error("SetLApic")]
311 SetLApic(#[source] nix::Error),
312 #[error("GetXsave")]
313 GetXsave(#[source] nix::Error),
314 #[error("SetXsave")]
315 SetXsave(#[source] nix::Error),
316 #[error("GetDebugRegs")]
317 GetDebugRegs(#[source] nix::Error),
318 #[error("SetDebugRegs")]
319 SetDebugRegs(#[source] nix::Error),
320 #[error("GetXcrs")]
321 GetXcrs(#[source] nix::Error),
322 #[error("SetXcrs")]
323 SetXcrs(#[source] nix::Error),
324 #[error("xsave is not enabled")]
325 XsaveNotEnabled,
326 #[error("SetGsiRouting")]
327 SetGsiRouting(#[source] nix::Error),
328 #[error("IrqLine")]
329 IrqLine(#[source] nix::Error),
330 #[error("GetMsrs")]
331 GetMsrs(#[source] nix::Error),
332 #[error("SetMsrs")]
333 SetMsrs(#[source] nix::Error),
334 #[error(
335 "MSR access only processed {completed} of {requested} entries (first failed MSR: {failed_msr:#x}, write={write})"
336 )]
337 IncompleteMsrs {
338 write: bool,
339 completed: usize,
340 requested: usize,
341 failed_msr: u32,
342 },
343 #[error("SetupMce")]
344 SetupMce(#[source] nix::Error),
345 #[error("GetMceCapSupported")]
346 GetMceCapSupported(#[source] nix::Error),
347 #[error("GetMpState")]
348 GetMpState(#[source] nix::Error),
349 #[error("SetMpState")]
350 SetMpState(#[source] nix::Error),
351 #[error("GetVcpuEvents")]
352 GetVcpuEvents(#[source] nix::Error),
353 #[error("SetVcpuEvents")]
354 SetVcpuEvents(#[source] nix::Error),
355 #[error("TranslateGva")]
356 TranslateGva(#[source] nix::Error),
357 #[error("unknown exit {0:#x}")]
358 UnknownExit(u32),
359 #[error("unknown Hyper-V exit {0:#x}")]
360 UnknownHvExit(u32),
361 #[error("ioeventfd")]
362 IoEventFd(#[source] nix::Error),
363 #[error("irqfd")]
364 IrqFd(#[source] nix::Error),
365 #[error("failed to set BSP")]
366 SetBsp(#[source] nix::Error),
367 #[error("CreateDevice")]
368 CreateDevice(#[source] nix::Error),
369 #[error("SetDeviceAttr")]
370 SetDeviceAttr(#[source] nix::Error),
371 #[error("GetDeviceAttr")]
372 GetDeviceAttr(#[source] nix::Error),
373 #[error("CheckExtension")]
374 CheckExtension(#[source] nix::Error),
375 #[error("GetClock")]
376 GetClock(#[source] nix::Error),
377 #[error("SetClock")]
378 SetClock(#[source] nix::Error),
379}
380
381type Result<T, E = Error> = std::result::Result<T, E>;
382
383#[derive(Debug)]
384struct Vp {
385 vcpu: File,
386 run_data: VpPtr,
387 thread: RwLock<Option<Pthread>>,
388 _phantom: PhantomData<kvm_run>,
389}
390
391#[derive(Debug)]
393struct VpPtr {
394 ptr: *mut kvm_run,
395 len: usize,
396}
397
398unsafe impl Send for VpPtr {}
403unsafe impl Sync for VpPtr {}
405
406#[derive(Debug)]
408pub struct Kvm(File);
409
410impl Kvm {
411 pub fn new() -> Result<Self> {
413 let kvm = std::fs::OpenOptions::new()
414 .read(true)
415 .write(true)
416 .open("/dev/kvm")
417 .map_err(Error::OpenKvm)?;
418
419 Ok(Self(kvm))
420 }
421
422 #[cfg(target_arch = "x86_64")]
424 pub fn supported_cpuid(&self) -> Result<Vec<kvm_cpuid_entry2>> {
425 const MAX_CPUID_ENTRIES: usize = 256;
426 let mut supported_cpuid = Cpuid {
427 cpuid: kvm_cpuid2 {
428 nent: MAX_CPUID_ENTRIES as u32,
429 ..Default::default()
430 },
431 entries: [Default::default(); MAX_CPUID_ENTRIES],
432 };
433
434 unsafe {
437 ioctl::kvm_get_supported_cpuid(self.as_fd().as_raw_fd(), &mut supported_cpuid.cpuid)
438 .map_err(Error::GetSupportedCpuid)?;
439 }
440
441 Ok(supported_cpuid.entries[..supported_cpuid.cpuid.nent as usize].to_vec())
442 }
443
444 #[cfg(target_arch = "x86_64")]
448 pub fn supported_mce_cap(&self) -> Result<u64> {
449 let mut cap: u64 = 0;
450 unsafe {
452 ioctl::kvm_x86_get_mce_cap_supported(self.as_fd().as_raw_fd(), &mut cap)
453 .map_err(Error::GetMceCapSupported)?;
454 }
455 Ok(cap)
456 }
457
458 #[cfg(target_arch = "x86_64")]
460 pub fn supported_sev_vmsa_features(&self) -> Result<u64> {
461 let mut value = 0u64;
462 let attr = kvm_device_attr {
463 group: KVM_X86_GRP_SEV,
464 attr: u64::from(KVM_X86_SEV_VMSA_FEATURES),
465 addr: std::ptr::from_mut(&mut value) as u64,
466 flags: 0,
467 };
468 unsafe {
470 ioctl::kvm_get_device_attr(self.as_fd().as_raw_fd(), &attr)
471 .map_err(Error::GetDeviceAttr)?;
472 }
473 Ok(value)
474 }
475
476 #[cfg(target_arch = "x86_64")]
479 pub fn supported_hv_cpuid(&self) -> Result<Vec<kvm_cpuid_entry2>> {
480 const MAX_CPUID_ENTRIES: usize = 256;
481 let mut supported_cpuid = Cpuid {
482 cpuid: kvm_cpuid2 {
483 nent: MAX_CPUID_ENTRIES as u32,
484 ..Default::default()
485 },
486 entries: [Default::default(); MAX_CPUID_ENTRIES],
487 };
488
489 unsafe {
491 ioctl::kvm_get_supported_hv_cpuid(self.as_fd().as_raw_fd(), &mut supported_cpuid.cpuid)
492 .map_err(Error::GetSupportedCpuid)?;
493 }
494
495 Ok(supported_cpuid.entries[..supported_cpuid.cpuid.nent as usize].to_vec())
496 }
497
498 pub fn check_extension(&self, extension: u32) -> nix::Result<libc::c_int> {
499 unsafe { ioctl::kvm_check_extension(self.as_fd().as_raw_fd(), extension as i32) }
501 }
502
503 pub fn new_vm(&self, vm_type: VmType) -> Result<Partition> {
504 let raw_vm_type = self.raw_vm_type(vm_type)?;
505 self.new_vm_with_type(raw_vm_type)
506 }
507
508 fn raw_vm_type(&self, vm_type: VmType) -> Result<libc::c_int> {
509 match vm_type {
510 VmType::Default => Ok(self.default_vm_type()),
511 #[cfg(target_arch = "x86_64")]
512 VmType::Snp => {
513 let supported_vm_types =
514 self.check_extension(KVM_CAP_VM_TYPES_UAPI)
515 .map_err(Error::CheckExtension)? as u64;
516 let raw_vm_type = KVM_X86_SNP_VM_UAPI;
517 let vm_type_bit = 1_u64
518 .checked_shl(raw_vm_type as u32)
519 .ok_or(Error::UnsupportedVmType(vm_type))?;
520 if supported_vm_types & vm_type_bit == 0 {
521 return Err(Error::UnsupportedVmType(vm_type));
522 }
523 Ok(raw_vm_type)
524 }
525 #[cfg(target_arch = "aarch64")]
526 VmType::Realm { ipa_bits } => Ok((KVM_VM_TYPE_ARM_REALM_UAPI
527 | ((ipa_bits as u64) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK_UAPI))
528 as libc::c_int),
529 }
530 }
531
532 fn default_vm_type(&self) -> libc::c_int {
533 #[cfg(target_arch = "aarch64")]
538 {
539 self.check_extension(KVM_CAP_ARM_VM_IPA_SIZE).unwrap_or(0)
540 }
541 #[cfg(not(target_arch = "aarch64"))]
542 {
543 0
544 }
545 }
546
547 fn new_vm_with_type(&self, vm_type: libc::c_int) -> Result<Partition> {
548 let vm = unsafe {
550 let fd =
551 ioctl::kvm_create_vm(self.as_fd().as_raw_fd(), vm_type).map_err(Error::CreateVm)?;
552 File::from_raw_fd(fd)
553 };
554
555 #[cfg(target_arch = "x86_64")]
559 unsafe {
560 ioctl::kvm_enable_cap(
563 vm.as_raw_fd(),
564 &kvm_enable_cap {
565 cap: KVM_CAP_DISABLE_QUIRKS,
566 args: [KVM_X86_QUIRK_LINT0_REENABLED.into(), 0, 0, 0],
567 ..Default::default()
568 },
569 )
570 .map_err(|err| Error::EnableCap("disable_quirks", err))?;
571 }
572
573 let mmap_size = unsafe {
575 ioctl::kvm_get_vcpu_mmap_size(self.as_fd().as_raw_fd(), 0)
576 .map_err(Error::GetVCpuMmapSize)? as usize
577 };
578
579 Ok(Partition {
580 vm,
581 vps: Vec::new(),
582 mmap_size,
583 })
584 }
585}
586
587impl AsFd for Kvm {
588 fn as_fd(&self) -> BorrowedFd<'_> {
589 self.0.as_fd()
590 }
591}
592
593impl From<File> for Kvm {
594 fn from(fd: File) -> Self {
595 Self(fd)
596 }
597}
598
599impl From<Kvm> for File {
600 fn from(kvm: Kvm) -> Self {
601 kvm.0
602 }
603}
604
605#[repr(C)]
606#[cfg(target_arch = "x86_64")]
607struct Cpuid {
608 cpuid: kvm_cpuid2,
609 entries: [kvm_cpuid_entry2; 256],
610}
611
612#[derive(Debug)]
613pub struct Partition {
614 vm: File,
615 vps: Vec<Option<Vp>>,
616 mmap_size: usize,
617}
618
619impl Partition {
620 #[cfg(target_arch = "x86_64")]
621 pub fn check_sev_snp_launch_extensions(&self) -> Result<()> {
622 unsafe { ioctl::kvm_memory_encrypt_op_supported(self.vm.as_raw_fd()) }.map_err(|err| {
625 Error::MemoryEncryptOp {
626 command: "KVM_MEMORY_ENCRYPT_OP(NULL)",
627 firmware_error: 0,
628 source: err,
629 }
630 })
631 }
632
633 #[cfg(target_arch = "x86_64")]
634 pub fn enable_hypercall_exits(&self, hypercall_mask: u64) -> Result<()> {
635 unsafe {
637 ioctl::kvm_enable_cap(
638 self.vm.as_raw_fd(),
639 &kvm_enable_cap {
640 cap: KVM_CAP_EXIT_HYPERCALL_UAPI,
641 args: [hypercall_mask, 0, 0, 0],
642 ..Default::default()
643 },
644 )
645 .map_err(|err| Error::EnableCap("exit_hypercall", err))?;
646 }
647 Ok(())
648 }
649
650 pub fn check_extension(&self, extension: u32) -> nix::Result<libc::c_int> {
651 unsafe { ioctl::kvm_check_extension(self.vm.as_raw_fd(), extension as i32) }
653 }
654
655 #[cfg(target_arch = "aarch64")]
656 pub fn arm_rmi_populate(&self, populate: &mut KvmArmRmiPopulate) -> Result<()> {
657 unsafe { ioctl::kvm_arm_rmi_populate(self.vm.as_raw_fd(), populate) }
660 .map_err(Error::ArmRmiPopulate)?;
661 Ok(())
662 }
663
664 #[cfg(target_arch = "x86_64")]
665 pub fn sev_snp_init(&self, sev: BorrowedFd<'_>, vmsa_features: u64) -> Result<()> {
666 let mut init = kvm_sev_init {
667 vmsa_features,
668 ..Default::default()
669 };
670 let mut command = kvm_sev_cmd {
671 id: sev_cmd_id_KVM_SEV_INIT2,
672 data: std::ptr::from_mut(&mut init) as u64,
673 sev_fd: sev.as_raw_fd() as u32,
674 ..Default::default()
675 };
676
677 unsafe {
680 ioctl::kvm_memory_encrypt_op(self.vm.as_raw_fd(), &mut command).map_err(|err| {
681 Error::MemoryEncryptOp {
682 command: "KVM_SEV_INIT2",
683 firmware_error: command.error,
684 source: err,
685 }
686 })?;
687 }
688 Ok(())
689 }
690
691 #[cfg(target_arch = "x86_64")]
692 fn sev_snp_cmd<T>(
693 &self,
694 sev: BorrowedFd<'_>,
695 command_name: &'static str,
696 command_id: sev_cmd_id,
697 data: &mut T,
698 ) -> Result<()> {
699 let mut command = kvm_sev_cmd {
700 id: command_id,
701 data: std::ptr::from_mut(data) as u64,
702 sev_fd: sev.as_raw_fd() as u32,
703 ..Default::default()
704 };
705
706 loop {
707 match unsafe { ioctl::kvm_memory_encrypt_op(self.vm.as_raw_fd(), &mut command) } {
710 Ok(_) => break,
711 Err(nix::errno::Errno::EAGAIN) => {}
712 Err(err) => {
713 return Err(Error::MemoryEncryptOp {
714 command: command_name,
715 firmware_error: command.error,
716 source: err,
717 });
718 }
719 }
720 }
721 Ok(())
722 }
723
724 #[cfg(target_arch = "x86_64")]
725 pub fn sev_snp_launch_start(
726 &self,
727 sev: BorrowedFd<'_>,
728 data: &mut kvm_sev_snp_launch_start,
729 ) -> Result<()> {
730 self.sev_snp_cmd(
731 sev,
732 "KVM_SEV_SNP_LAUNCH_START",
733 sev_cmd_id_KVM_SEV_SNP_LAUNCH_START,
734 data,
735 )
736 }
737
738 #[cfg(target_arch = "x86_64")]
739 pub fn sev_snp_launch_update(
740 &self,
741 sev: BorrowedFd<'_>,
742 gfn_start: u64,
743 uaddr: u64,
744 len: u64,
745 page_type: SevSnpPageType,
746 ) -> Result<()> {
747 let mut update = kvm_sev_snp_launch_update {
748 gfn_start,
749 uaddr,
750 len,
751 type_: page_type.as_uapi(),
752 ..Default::default()
753 };
754
755 while update.len != 0 {
756 self.sev_snp_cmd(
757 sev,
758 "KVM_SEV_SNP_LAUNCH_UPDATE",
759 sev_cmd_id_KVM_SEV_SNP_LAUNCH_UPDATE,
760 &mut update,
761 )?;
762 }
763 Ok(())
764 }
765
766 #[cfg(target_arch = "x86_64")]
767 pub fn sev_snp_launch_finish(
768 &self,
769 sev: BorrowedFd<'_>,
770 data: &mut kvm_sev_snp_launch_finish,
771 ) -> Result<()> {
772 self.sev_snp_cmd(
773 sev,
774 "KVM_SEV_SNP_LAUNCH_FINISH",
775 sev_cmd_id_KVM_SEV_SNP_LAUNCH_FINISH,
776 data,
777 )
778 }
779
780 pub fn enable_split_irqchip(&self, lines: u32) -> Result<()> {
781 unsafe {
785 ioctl::kvm_enable_cap(
786 self.vm.as_raw_fd(),
787 &kvm_enable_cap {
788 cap: KVM_CAP_SPLIT_IRQCHIP,
789 args: [lines.into(), 0, 0, 0],
790 ..Default::default()
791 },
792 )
793 .map_err(|err| Error::EnableCap("split_irqchip", err))?;
794 }
795 Ok(())
796 }
797
798 #[cfg(target_arch = "x86_64")]
800 pub fn enable_x2apic_api(&self) -> Result<()> {
801 let flags = KVM_X2APIC_API_USE_32BIT_IDS;
802 unsafe {
804 ioctl::kvm_enable_cap(
805 self.vm.as_raw_fd(),
806 &kvm_enable_cap {
807 cap: KVM_CAP_X2APIC_API,
808 args: [flags.into(), 0, 0, 0],
809 ..Default::default()
810 },
811 )
812 .map_err(|err| Error::EnableCap("x2apic_api", err))?;
813 }
814 Ok(())
815 }
816
817 pub fn enable_unknown_msr_exits(&self) -> Result<()> {
818 unsafe {
821 ioctl::kvm_enable_cap(
822 self.vm.as_raw_fd(),
823 &kvm_enable_cap {
824 cap: KVM_CAP_X86_USER_SPACE_MSR,
825 args: [KVM_MSR_EXIT_REASON_UNKNOWN.into(), 0, 0, 0],
826 ..Default::default()
827 },
828 )
829 .map_err(|err| Error::EnableCap("user_space_msr", err))?;
830 }
831 Ok(())
832 }
833
834 #[cfg(target_arch = "x86_64")]
837 pub fn set_bsp(&mut self, vcpu_idx: u32) -> Result<()> {
838 unsafe {
840 ioctl::kvm_set_boot_cpu_id(self.vm.as_raw_fd(), vcpu_idx as i32)
841 .map_err(Error::SetBsp)?;
842 }
843
844 Ok(())
845 }
846
847 pub fn add_vp(&mut self, vcpu_idx: u32) -> Result<()> {
848 let vcpu = unsafe {
851 let fd = ioctl::kvm_create_vcpu(self.vm.as_raw_fd(), vcpu_idx as i32)
852 .map_err(Error::CreateVCpu)?;
853 File::from_raw_fd(fd)
854 };
855
856 let ptr = unsafe {
858 let ptr = libc::mmap(
859 std::ptr::null_mut(),
860 self.mmap_size,
861 libc::PROT_READ | libc::PROT_WRITE,
862 libc::MAP_SHARED,
863 vcpu.as_raw_fd(),
864 0,
865 );
866 if ptr == libc::MAP_FAILED {
867 return Err(Error::MmapVCpu(io::Error::last_os_error()));
868 }
869 ptr
870 };
871
872 #[cfg(target_arch = "aarch64")]
873 {
874 let mut kvi = kvm_vcpu_init::default();
876 kvi.features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2;
877
878 if vcpu_idx > 0 {
879 kvi.features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF;
880 }
881
882 let mut pref_target = kvm_vcpu_init::default();
883 unsafe {
885 ioctl::kvm_arm_preferred_target(self.vm.as_raw_fd(), &mut pref_target)
886 .map_err(Error::CreateVCpu)?
887 };
888
889 kvi.target = pref_target.target;
890 unsafe { ioctl::kvm_arm_vcpu_init(vcpu.as_raw_fd(), &kvi).map_err(Error::CreateVCpu)? };
892 }
893
894 let vp = Vp {
895 vcpu,
896 run_data: VpPtr {
897 ptr: ptr.cast(),
898 len: self.mmap_size,
899 },
900 thread: RwLock::new(None),
901 _phantom: PhantomData,
902 };
903 if self.vps.len() <= vcpu_idx as usize {
904 self.vps.resize_with(vcpu_idx as usize + 1, || None);
905 }
906 assert!(self.vps[vcpu_idx as usize].replace(vp).is_none());
907
908 Ok(())
909 }
910
911 pub fn vp(&self, index: u32) -> Processor<'_> {
912 Processor(self, index)
913 }
914
915 pub fn request_msi(&self, msi: &kvm_msi) -> Result<()> {
916 unsafe {
919 ioctl::kvm_signal_msi(self.vm.as_raw_fd(), msi).map_err(Error::SignalMsi)?;
920 }
921 Ok(())
922 }
923
924 pub unsafe fn set_user_memory_region(
933 &self,
934 slot: u32,
935 data: *mut u8,
936 size: usize,
937 addr: u64,
938 readonly: bool,
939 ) -> Result<()> {
940 let region = kvm_userspace_memory_region {
941 slot,
942 flags: if readonly { KVM_MEM_READONLY } else { 0 },
943 guest_phys_addr: addr,
944 memory_size: size as u64,
945 userspace_addr: data as usize as u64,
946 };
947 unsafe {
950 ioctl::kvm_set_user_memory_region(self.vm.as_raw_fd(), ®ion)
951 .map_err(Error::SetMemoryRegion)?;
952 }
953 Ok(())
954 }
955
956 pub unsafe fn set_user_memory_region2(
967 &self,
968 slot: u32,
969 data: *mut u8,
970 size: usize,
971 addr: u64,
972 readonly: bool,
973 guest_memfd: Option<(&File, u64)>,
974 ) -> Result<()> {
975 let (guest_memfd, guest_memfd_offset, guest_memfd_flag) = guest_memfd
976 .map(|(file, offset)| (file.as_raw_fd() as u32, offset, KVM_MEM_GUEST_MEMFD))
977 .unwrap_or((0, 0, 0));
978 let region = kvm_userspace_memory_region2 {
979 slot,
980 flags: if readonly { KVM_MEM_READONLY } else { 0 } | guest_memfd_flag,
981 guest_phys_addr: addr,
982 memory_size: size as u64,
983 userspace_addr: data as usize as u64,
984 guest_memfd_offset,
985 guest_memfd,
986 ..Default::default()
987 };
988 unsafe {
992 ioctl::kvm_set_user_memory_region2(self.vm.as_raw_fd(), ®ion)
993 .map_err(Error::SetMemoryRegion)?;
994 }
995 Ok(())
996 }
997
998 pub fn create_guest_memfd(&self, size: u64) -> Result<File> {
999 let mut guest_memfd = kvm_create_guest_memfd {
1000 size,
1001 ..Default::default()
1002 };
1003 let fd = unsafe {
1005 ioctl::kvm_create_guest_memfd(self.vm.as_raw_fd(), &mut guest_memfd)
1006 .map_err(Error::CreateGuestMemfd)?
1007 };
1008 Ok(unsafe { File::from_raw_fd(fd) })
1010 }
1011
1012 pub fn set_memory_attributes(&self, addr: u64, size: u64, attributes: u64) -> Result<()> {
1013 let attr = kvm_memory_attributes {
1014 address: addr,
1015 size,
1016 attributes,
1017 ..Default::default()
1018 };
1019 unsafe {
1021 ioctl::kvm_set_memory_attributes(self.vm.as_raw_fd(), &attr)
1022 .map_err(Error::SetMemoryAttributes)?;
1023 }
1024 Ok(())
1025 }
1026
1027 pub fn set_gsi_routes(&self, routes: &[(u32, RoutingEntry)]) -> Result<()> {
1028 const MAX_ROUTES: usize = 2048;
1029 assert!(routes.len() <= MAX_ROUTES);
1030
1031 #[repr(C)]
1032 struct Routes {
1033 header: kvm_irq_routing,
1034 entries: [kvm_irq_routing_entry; MAX_ROUTES],
1035 }
1036
1037 let mut kvm_routes = Routes {
1038 header: Default::default(),
1039 entries: [Default::default(); MAX_ROUTES],
1040 };
1041 for (i, route) in routes.iter().enumerate() {
1042 let (type_, flags, u) = match route.1 {
1043 RoutingEntry::Msi {
1044 address_lo,
1045 address_hi,
1046 data,
1047 devid,
1048 } => {
1049 let (flags, anon) = if let Some(devid) = devid {
1050 (
1051 KVM_MSI_VALID_DEVID,
1052 kvm_irq_routing_msi__bindgen_ty_1 { devid },
1053 )
1054 } else {
1055 (0, Default::default())
1056 };
1057 (
1058 KVM_IRQ_ROUTING_MSI,
1059 flags,
1060 kvm_irq_routing_entry__bindgen_ty_1 {
1061 msi: kvm_irq_routing_msi {
1062 address_lo,
1063 address_hi,
1064 data,
1065 __bindgen_anon_1: anon,
1066 },
1067 },
1068 )
1069 }
1070 RoutingEntry::HvSint { vp, sint } => (
1071 KVM_IRQ_ROUTING_HV_SINT,
1072 0,
1073 kvm_irq_routing_entry__bindgen_ty_1 {
1074 hv_sint: kvm_irq_routing_hv_sint {
1075 vcpu: vp,
1076 sint: sint.into(),
1077 },
1078 },
1079 ),
1080 RoutingEntry::Irqchip { pin } => (
1081 KVM_IRQ_ROUTING_IRQCHIP,
1082 0,
1083 kvm_irq_routing_entry__bindgen_ty_1 {
1084 irqchip: kvm_irq_routing_irqchip { pin, irqchip: 0 },
1085 },
1086 ),
1087 };
1088 kvm_routes.entries[i] = kvm_irq_routing_entry {
1089 gsi: route.0,
1090 type_,
1091 flags,
1092 pad: 0,
1093 u,
1094 };
1095 kvm_routes.header.nr += 1;
1096 }
1097
1098 unsafe {
1101 ioctl::kvm_set_gsi_routing(self.vm.as_raw_fd(), &kvm_routes.header)
1102 .map_err(Error::SetGsiRouting)?;
1103 }
1104 Ok(())
1105 }
1106
1107 pub fn irqfd(&self, gsi: u32, event: RawFd, assign: bool) -> Result<()> {
1108 unsafe {
1111 ioctl::kvm_irqfd(
1112 self.vm.as_raw_fd(),
1113 &kvm_irqfd {
1114 fd: event as u32,
1115 gsi,
1116 flags: if assign { 0 } else { KVM_IRQFD_FLAG_DEASSIGN },
1117 resamplefd: 0,
1118 pad: [0; 16],
1119 },
1120 )
1121 .map_err(Error::IrqFd)
1122 .map(drop)
1123 }
1124 }
1125
1126 pub fn irq_line(&self, gsi: u32, level: bool) -> Result<()> {
1127 unsafe {
1130 ioctl::kvm_irq_line(
1131 self.vm.as_raw_fd(),
1132 &kvm_irq_level {
1133 __bindgen_anon_1: kvm_irq_level__bindgen_ty_1 { irq: gsi },
1134 level: level.into(),
1135 },
1136 )
1137 .map_err(Error::IrqLine)?;
1138 }
1139 Ok(())
1140 }
1141
1142 pub fn ioeventfd(
1143 &self,
1144 datamatch: u64,
1145 addr: u64,
1146 len: u32,
1147 fd: i32,
1148 flags: u32,
1149 ) -> Result<()> {
1150 unsafe {
1153 ioctl::kvm_ioeventfd(
1154 self.vm.as_raw_fd(),
1155 &kvm_ioeventfd {
1156 datamatch,
1157 addr,
1158 len,
1159 fd,
1160 flags,
1161 ..Default::default()
1162 },
1163 )
1164 .map_err(Error::IoEventFd)?;
1165 };
1166 Ok(())
1167 }
1168
1169 pub fn create_device(&self, ty: u32, flags: u32) -> nix::Result<Device> {
1170 unsafe {
1175 let mut device = kvm_create_device {
1176 type_: ty,
1177 fd: 0,
1178 flags,
1179 };
1180 ioctl::kvm_create_device(self.vm.as_raw_fd(), &mut device)?;
1181 Ok(Device(File::from_raw_fd(device.fd as i32)))
1182 }
1183 }
1184
1185 pub fn test_create_device(&self, ty: u32) -> nix::Result<()> {
1190 unsafe {
1194 let mut device = kvm_create_device {
1195 type_: ty,
1196 fd: 0,
1197 flags: KVM_CREATE_DEVICE_TEST,
1198 };
1199 ioctl::kvm_create_device(self.vm.as_raw_fd(), &mut device)?;
1200 }
1201 Ok(())
1202 }
1203
1204 pub fn get_clock_ns(&self) -> Result<kvm_clock_data> {
1206 let mut clock = kvm_clock_data::default();
1207 unsafe {
1209 ioctl::kvm_get_clock(self.vm.as_raw_fd(), &mut clock).map_err(Error::GetClock)?;
1210 }
1211 Ok(clock)
1212 }
1213
1214 pub fn set_clock_ns(&self, clock_ns: u64) -> Result<()> {
1216 let clock = kvm_clock_data {
1217 clock: clock_ns,
1218 ..Default::default()
1219 };
1220 unsafe {
1222 ioctl::kvm_set_clock(self.vm.as_raw_fd(), &clock).map_err(Error::SetClock)?;
1223 }
1224 Ok(())
1225 }
1226}
1227
1228pub struct Device(File);
1230
1231impl Device {
1232 pub unsafe fn set_device_attr<T>(
1237 &self,
1238 group: u32,
1239 attr: u32,
1240 addr: &T,
1241 flags: u32,
1242 ) -> nix::Result<()> {
1243 unsafe {
1245 ioctl::kvm_set_device_attr(
1246 self.0.as_raw_fd(),
1247 &kvm_device_attr {
1248 group,
1249 attr: attr as u64,
1250 addr: std::ptr::from_ref(addr) as u64,
1251 flags,
1252 },
1253 )?;
1254 }
1255 Ok(())
1256 }
1257}
1258
1259#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1260pub enum RoutingEntry {
1261 Irqchip {
1262 pin: u32,
1263 },
1264 Msi {
1265 address_lo: u32,
1266 address_hi: u32,
1267 data: u32,
1268 devid: Option<u32>,
1269 },
1270 HvSint {
1271 vp: u32,
1272 sint: u8,
1273 },
1274}
1275
1276pub struct Processor<'a>(&'a Partition, u32);
1277
1278impl<'a> Processor<'a> {
1279 pub fn enable_synic(&self) -> Result<()> {
1280 unsafe {
1284 ioctl::kvm_enable_cap(
1285 self.get().vcpu.as_raw_fd(),
1286 &kvm_enable_cap {
1287 cap: KVM_CAP_HYPERV_SYNIC2,
1288 ..Default::default()
1289 },
1290 )
1291 .map_err(|err| Error::EnableCap("hyperv_synic2", err))?;
1292 }
1293 Ok(())
1294 }
1295
1296 #[cfg(target_arch = "x86_64")]
1297 pub fn set_cpuid(&self, entries: &[kvm_cpuid_entry2]) -> Result<()> {
1298 const MAX_CPUID_ENTRIES: usize = 256;
1299 assert!(entries.len() <= MAX_CPUID_ENTRIES);
1300
1301 let mut cpuid: Cpuid = Cpuid {
1302 cpuid: Default::default(),
1303 entries: [Default::default(); MAX_CPUID_ENTRIES],
1304 };
1305 for (i, e) in entries.iter().enumerate() {
1306 cpuid.entries[i] = *e;
1307 cpuid.cpuid.nent += 1;
1308 }
1309
1310 unsafe {
1312 ioctl::kvm_set_cpuid2(self.get().vcpu.as_raw_fd(), &cpuid.cpuid)
1313 .map_err(Error::SetCpuid)?;
1314 }
1315 Ok(())
1316 }
1317
1318 fn get(&self) -> &'a Vp {
1319 self.0.vps[self.1 as usize].as_ref().expect("vp exists")
1320 }
1321
1322 pub fn force_exit(&self) {
1327 let vp = self.get();
1328 let thread = vp.thread.read();
1329 if let Some(thread) = *thread {
1330 if thread != Pthread::current() {
1331 thread
1332 .signal(libc::SIGRTMIN())
1333 .expect("thread cancel signal failed");
1334 }
1335 }
1336 }
1337
1338 pub fn interrupt(&self, vector: u32) -> Result<()> {
1339 unsafe {
1341 ioctl::kvm_interrupt(self.get().vcpu.as_raw_fd(), &kvm_interrupt { irq: vector })
1342 .map_err(Error::Interrupt)?;
1343 };
1344 Ok(())
1345 }
1346
1347 pub fn set_reg64(&self, reg_id: u64, value: u64) -> Result<()> {
1349 let reg = kvm_one_reg {
1350 id: reg_id,
1351 addr: std::ptr::from_ref(&value) as u64,
1352 };
1353 unsafe {
1355 ioctl::kvm_set_reg(self.get().vcpu.as_raw_fd(), ®).map_err(Error::SetRegs)?;
1356 }
1357 Ok(())
1358 }
1359
1360 #[cfg(not(target_arch = "aarch64"))]
1361 pub fn set_regs(&self, regs: &kvm_regs) -> Result<()> {
1362 unsafe {
1365 ioctl::kvm_set_regs(self.get().vcpu.as_raw_fd(), regs).map_err(Error::SetRegs)?;
1366 }
1367 Ok(())
1368 }
1369
1370 #[cfg(not(target_arch = "aarch64"))]
1371 pub fn set_sregs(&self, sregs: &kvm_sregs) -> Result<()> {
1372 unsafe {
1375 ioctl::kvm_set_sregs(self.get().vcpu.as_raw_fd(), sregs).map_err(Error::SetRegs)?;
1376 }
1377 Ok(())
1378 }
1379
1380 pub fn get_reg64(&self, reg_id: u64) -> Result<u64> {
1382 let mut value: u64 = 0;
1383 let reg = kvm_one_reg {
1384 id: reg_id,
1385 addr: std::ptr::from_mut(&mut value) as u64,
1386 };
1387 unsafe {
1389 ioctl::kvm_get_reg(self.get().vcpu.as_raw_fd(), ®).map_err(Error::GetRegs)?;
1390 }
1391
1392 Ok(value)
1393 }
1394
1395 #[cfg(not(target_arch = "aarch64"))]
1396 pub fn get_regs(&self) -> Result<kvm_regs> {
1397 let mut regs = Default::default();
1398 unsafe {
1401 ioctl::kvm_get_regs(self.get().vcpu.as_raw_fd(), &mut regs).map_err(Error::GetRegs)?;
1402 }
1403 Ok(regs)
1404 }
1405
1406 #[cfg(not(target_arch = "aarch64"))]
1407 pub fn get_sregs(&self) -> Result<kvm_sregs> {
1408 let mut sregs = Default::default();
1409 unsafe {
1412 ioctl::kvm_get_sregs(self.get().vcpu.as_raw_fd(), &mut sregs)
1413 .map_err(Error::GetSRegs)?;
1414 }
1415 Ok(sregs)
1416 }
1417
1418 #[cfg(target_arch = "x86_64")]
1419 pub fn get_msrs(&self, msrs: &[u32], values: &mut [u64]) -> Result<()> {
1420 const MAX_MSR_ENTRIES: usize = 256;
1421 assert_eq!(msrs.len(), values.len());
1422 assert!(msrs.len() <= MAX_MSR_ENTRIES);
1423
1424 #[repr(C)]
1425 struct Msrs {
1426 header: kvm_msrs,
1427 entries: [kvm_msr_entry; MAX_MSR_ENTRIES],
1428 }
1429 let mut input = Msrs {
1430 header: kvm_msrs {
1431 nmsrs: msrs.len() as u32,
1432 ..Default::default()
1433 },
1434 entries: [Default::default(); MAX_MSR_ENTRIES],
1435 };
1436 for (i, msr) in msrs.iter().enumerate() {
1437 input.entries[i] = kvm_msr_entry {
1438 index: *msr,
1439 reserved: 0,
1440 data: 0,
1441 };
1442 }
1443
1444 let completed = unsafe {
1446 ioctl::kvm_get_msrs(self.get().vcpu.as_raw_fd(), &mut input.header)
1447 .map_err(Error::GetMsrs)?
1448 } as usize;
1449 assert!(completed <= msrs.len());
1450 if completed < msrs.len() {
1451 return Err(Error::IncompleteMsrs {
1452 requested: msrs.len(),
1453 completed,
1454 failed_msr: msrs.get(completed).copied().unwrap(),
1455 write: false,
1456 });
1457 }
1458 for (v, e) in values.iter_mut().zip(&input.entries) {
1459 *v = e.data;
1460 }
1461 Ok(())
1462 }
1463
1464 #[cfg(target_arch = "x86_64")]
1465 pub fn set_msrs(&self, msrs: &[(u32, u64)]) -> Result<()> {
1466 const MAX_MSR_ENTRIES: usize = 256;
1467 assert!(msrs.len() <= MAX_MSR_ENTRIES);
1468
1469 #[repr(C)]
1470 struct Msrs {
1471 header: kvm_msrs,
1472 entries: [kvm_msr_entry; MAX_MSR_ENTRIES],
1473 }
1474 let mut input = Msrs {
1475 header: kvm_msrs {
1476 nmsrs: msrs.len() as u32,
1477 ..Default::default()
1478 },
1479 entries: [Default::default(); MAX_MSR_ENTRIES],
1480 };
1481 for (i, msr) in msrs.iter().enumerate() {
1482 input.entries[i] = kvm_msr_entry {
1483 index: msr.0,
1484 reserved: 0,
1485 data: msr.1,
1486 };
1487 }
1488
1489 let completed = unsafe {
1491 ioctl::kvm_set_msrs(self.get().vcpu.as_raw_fd(), &input.header)
1492 .map_err(Error::SetMsrs)?
1493 } as usize;
1494 assert!(completed <= msrs.len());
1495 if completed < msrs.len() {
1496 return Err(Error::IncompleteMsrs {
1497 requested: msrs.len(),
1498 completed,
1499 failed_msr: msrs.get(completed).copied().unwrap().0,
1500 write: true,
1501 });
1502 }
1503 Ok(())
1504 }
1505
1506 #[cfg(target_arch = "x86_64")]
1513 pub fn setup_mce(&self, mcg_cap: u64) -> Result<()> {
1514 unsafe {
1516 ioctl::kvm_x86_setup_mce(self.get().vcpu.as_raw_fd(), &mcg_cap)
1517 .map_err(Error::SetupMce)?;
1518 }
1519 Ok(())
1520 }
1521
1522 #[cfg(target_arch = "x86_64")]
1523 pub fn get_lapic(&self, state: &mut [u8; 1024]) -> Result<()> {
1524 assert_eq!(size_of_val(state), size_of::<kvm_lapic_state>());
1525
1526 unsafe {
1529 ioctl::kvm_get_lapic(self.get().vcpu.as_raw_fd(), state.as_mut_ptr().cast())
1530 .map_err(Error::GetLApic)?;
1531 }
1532 Ok(())
1533 }
1534
1535 #[cfg(target_arch = "x86_64")]
1536 pub fn set_lapic(&self, state: &[u8; 1024]) -> Result<()> {
1537 assert_eq!(size_of_val(state), size_of::<kvm_lapic_state>());
1538
1539 unsafe {
1542 ioctl::kvm_set_lapic(self.get().vcpu.as_raw_fd(), state.as_ptr().cast())
1543 .map_err(Error::SetLApic)?;
1544 }
1545 Ok(())
1546 }
1547
1548 #[cfg(target_arch = "x86_64")]
1549 pub fn get_xsave(&self, state: &mut [u8; 4096]) -> Result<()> {
1550 assert_eq!(size_of_val(state), size_of::<kvm_xsave>());
1551
1552 unsafe {
1555 ioctl::kvm_get_xsave(self.get().vcpu.as_raw_fd(), state.as_mut_ptr().cast())
1556 .map_err(Error::GetXsave)?;
1557 }
1558 Ok(())
1559 }
1560
1561 #[cfg(target_arch = "x86_64")]
1562 pub fn set_xsave(&self, state: &[u8; 4096]) -> Result<()> {
1563 assert_eq!(size_of_val(state), size_of::<kvm_xsave>());
1564
1565 unsafe {
1568 ioctl::kvm_set_xsave(self.get().vcpu.as_raw_fd(), state.as_ptr().cast())
1569 .map_err(Error::SetXsave)?;
1570 }
1571 Ok(())
1572 }
1573
1574 #[cfg(target_arch = "x86_64")]
1575 pub fn set_debug_regs(&self, regs: &DebugRegisters) -> Result<()> {
1576 let data = kvm_debugregs {
1577 db: regs.db,
1578 dr6: regs.dr6,
1579 dr7: regs.dr7,
1580 flags: 0,
1581 reserved: [0; 9],
1582 };
1583
1584 unsafe {
1587 ioctl::kvm_set_debugregs(self.get().vcpu.as_raw_fd(), &data)
1588 .map_err(Error::SetDebugRegs)?;
1589 }
1590 Ok(())
1591 }
1592
1593 #[cfg(target_arch = "x86_64")]
1594 pub fn get_debug_regs(&self) -> Result<DebugRegisters> {
1595 let mut data = Default::default();
1596
1597 unsafe {
1600 ioctl::kvm_get_debugregs(self.get().vcpu.as_raw_fd(), &mut data)
1601 .map_err(Error::GetDebugRegs)?;
1602 }
1603
1604 Ok(DebugRegisters {
1605 db: data.db,
1606 dr6: data.dr6,
1607 dr7: data.dr7,
1608 })
1609 }
1610
1611 #[cfg(target_arch = "x86_64")]
1612 pub fn set_xcr0(&self, value: u64) -> Result<()> {
1613 let mut data = kvm_xcrs {
1614 nr_xcrs: 1,
1615 ..Default::default()
1616 };
1617 data.xcrs[0] = kvm_xcr {
1618 xcr: 0,
1619 reserved: 0,
1620 value,
1621 };
1622
1623 unsafe {
1626 ioctl::kvm_set_xcrs(self.get().vcpu.as_raw_fd(), &data).map_err(Error::GetXcrs)?;
1627 }
1628 Ok(())
1629 }
1630
1631 #[cfg(target_arch = "x86_64")]
1632 pub fn get_xcr0(&self) -> Result<u64> {
1633 let mut data = Default::default();
1634
1635 unsafe {
1638 ioctl::kvm_get_xcrs(self.get().vcpu.as_raw_fd(), &mut data).map_err(Error::SetXcrs)?;
1639 }
1640
1641 if data.nr_xcrs < 1 {
1642 return Err(Error::XsaveNotEnabled);
1643 }
1644 assert_eq!(data.nr_xcrs, 1);
1645 assert_eq!(data.xcrs[0].xcr, 0);
1646 Ok(data.xcrs[0].value)
1647 }
1648
1649 pub fn set_mp_state(&self, state: u32) -> Result<()> {
1650 let state = kvm_mp_state { mp_state: state };
1651 unsafe {
1654 ioctl::kvm_set_mp_state(self.get().vcpu.as_raw_fd(), &state)
1655 .map_err(Error::SetMpState)?;
1656 }
1657 Ok(())
1658 }
1659
1660 pub fn get_mp_state(&self) -> Result<u32> {
1661 let mut state = Default::default();
1662 unsafe {
1665 ioctl::kvm_get_mp_state(self.get().vcpu.as_raw_fd(), &mut state)
1666 .map_err(Error::GetMpState)?;
1667 }
1668 Ok(state.mp_state)
1669 }
1670
1671 pub fn set_vcpu_events(&self, events: &kvm_vcpu_events) -> Result<()> {
1672 unsafe {
1675 ioctl::kvm_set_vcpu_events(self.get().vcpu.as_raw_fd(), events)
1676 .map_err(Error::SetVcpuEvents)?;
1677 }
1678 Ok(())
1679 }
1680
1681 pub fn get_vcpu_events(&self) -> Result<kvm_vcpu_events> {
1682 let mut events = Default::default();
1683 unsafe {
1686 ioctl::kvm_get_vcpu_events(self.get().vcpu.as_raw_fd(), &mut events)
1687 .map_err(Error::GetVcpuEvents)?;
1688 }
1689 Ok(events)
1690 }
1691
1692 pub fn translate_gva(&self, gva: u64) -> Result<kvm_translation> {
1693 let mut translation = kvm_translation {
1694 linear_address: gva,
1695 ..Default::default()
1696 };
1697
1698 unsafe {
1700 ioctl::kvm_translation(self.get().vcpu.as_raw_fd(), &mut translation)
1701 .map_err(Error::TranslateGva)?;
1702 }
1703
1704 Ok(translation)
1705 }
1706
1707 #[cfg(target_arch = "x86_64")]
1710 pub fn set_guest_debug(&self, control: u32, db: [u64; 4], dr7: u64) -> Result<()> {
1711 let debug = kvm_guest_debug {
1713 control,
1714 pad: 0,
1715 arch: kvm_guest_debug_arch {
1716 debugreg: [db[0], db[1], db[2], db[3], 0, 0, 0, dr7],
1717 },
1718 };
1719
1720 unsafe {
1723 ioctl::kvm_set_guest_debug(self.get().vcpu.as_raw_fd(), &debug)
1724 .map_err(Error::GetRegs)?;
1725 }
1726 Ok(())
1727 }
1728
1729 pub unsafe fn set_device_attr<T>(
1734 &self,
1735 group: u32,
1736 attr: u32,
1737 addr: &T,
1738 flags: u32,
1739 ) -> nix::Result<libc::c_int> {
1740 unsafe {
1742 ioctl::kvm_set_device_attr(
1743 self.get().vcpu.as_raw_fd(),
1744 &kvm_device_attr {
1745 group,
1746 attr: attr as u64,
1747 addr: std::ptr::from_ref(addr) as u64,
1748 flags,
1749 },
1750 )
1751 }
1752 }
1753
1754 pub fn runner(&self) -> VpRunner<'a> {
1755 assert!(
1758 self.get()
1759 .thread
1760 .write()
1761 .replace(Pthread::current())
1762 .is_none()
1763 );
1764
1765 VpRunner {
1766 partition: self.0,
1767 idx: self.1,
1768 _not_send_sync: PhantomData,
1769 }
1770 }
1771}
1772
1773pub struct VpRunner<'a> {
1774 partition: &'a Partition,
1775 idx: u32,
1776 _not_send_sync: PhantomData<*const u8>,
1779}
1780
1781impl Drop for VpRunner<'_> {
1782 fn drop(&mut self) {
1783 let thread = self.get().thread.write().take();
1785 assert_eq!(thread, Some(Pthread::current()));
1786 }
1787}
1788
1789impl<'a> VpRunner<'a> {
1790 fn get(&self) -> &'a Vp {
1791 self.partition.vp(self.idx).get()
1792 }
1793
1794 fn run_data(&mut self) -> &mut kvm_run {
1795 let vp = self.get();
1796 unsafe { &mut *vp.run_data.ptr }
1801 }
1802
1803 #[cfg_attr(target_arch = "aarch64", expect(dead_code))]
1804 fn run_data_slice(&mut self) -> &mut [u8] {
1805 let vp = self.get();
1806 unsafe { std::slice::from_raw_parts_mut(vp.run_data.ptr.cast::<u8>(), vp.run_data.len) }
1811 }
1812
1813 fn run_vp_once(&mut self) -> Result<bool> {
1815 CURRENT_KVM_RUN.with(|r| {
1816 let vp = self.get();
1817
1818 self.run_data().immediate_exit = 0;
1821
1822 match r.swap(vp.run_data.ptr as usize, Ordering::Relaxed) {
1825 NO_KVM_RUN => {}
1826 CANCEL_KVM_RUN => {
1827 unsafe { set_immediate_exit(vp.run_data.ptr) };
1838 }
1839 state => unreachable!("unexpected state {:#x}", state),
1840 }
1841
1842 let result = unsafe { ioctl::kvm_run(vp.vcpu.as_raw_fd(), 0) };
1844 CURRENT_KVM_RUN.with(|r| r.store(NO_KVM_RUN, Ordering::Relaxed));
1845 match result {
1846 Ok(_) => Ok(true),
1847 Err(err) => match err {
1848 nix::errno::Errno::EINTR | nix::errno::Errno::EAGAIN => Ok(false),
1849 _ if self.run_data().exit_reason == KVM_EXIT_MEMORY_FAULT => {
1850 let memory_fault = unsafe { self.run_data().__bindgen_anon_1.memory_fault };
1852 Err(Error::RunMemoryFault {
1853 flags: memory_fault.flags,
1854 gpa: memory_fault.gpa,
1855 size: memory_fault.size,
1856 source: err,
1857 })
1858 }
1859 _ => Err(Error::Run(err)),
1860 },
1861 }
1862 })
1863 }
1864
1865 pub fn complete_exit(&mut self) -> Result<Exit<'_>, Error> {
1869 CURRENT_KVM_RUN.with(|run| run.store(CANCEL_KVM_RUN, Ordering::Relaxed));
1870 self.run()
1871 }
1872
1873 pub fn run(&mut self) -> Result<Exit<'_>, Error> {
1878 if !self.run_vp_once()? {
1879 return Ok(Exit::Interrupted);
1880 }
1881
1882 let exit = match self.run_data().exit_reason {
1883 #[cfg(target_arch = "x86_64")]
1884 KVM_EXIT_DEBUG => {
1885 let debug = unsafe { &self.run_data().__bindgen_anon_1.debug };
1887
1888 Exit::Debug {
1889 exception: debug.arch.exception,
1890 pc: debug.arch.pc,
1891 dr6: debug.arch.dr6,
1892 dr7: debug.arch.dr7,
1893 }
1894 }
1895 #[cfg(target_arch = "x86_64")]
1896 KVM_EXIT_IO => {
1897 let io = unsafe { self.run_data().__bindgen_anon_1.io };
1899
1900 let offset = io.data_offset as usize;
1901 let data = &mut self.run_data_slice()
1902 [offset..offset + io.size as usize * io.count as usize];
1903 if io.direction == KVM_EXIT_IO_IN as u8 {
1904 Exit::IoIn {
1905 port: io.port,
1906 size: io.size,
1907 data,
1908 }
1909 } else {
1910 Exit::IoOut {
1911 port: io.port,
1912 size: io.size,
1913 data,
1914 }
1915 }
1916 }
1917 #[cfg(target_arch = "x86_64")]
1918 KVM_EXIT_IRQ_WINDOW_OPEN => {
1919 let rdata = self.run_data();
1920 assert!(rdata.ready_for_interrupt_injection != 0);
1921 rdata.request_interrupt_window = 0;
1922 Exit::InterruptWindow
1923 }
1924 KVM_EXIT_MMIO => {
1925 let mmio = unsafe { &mut self.run_data().__bindgen_anon_1.mmio };
1927 if mmio.is_write != 0 {
1928 Exit::MmioWrite {
1929 address: mmio.phys_addr,
1930 data: &mmio.data[0..mmio.len as usize],
1931 }
1932 } else {
1933 mmio.data = [0; 8];
1934 Exit::MmioRead {
1935 address: mmio.phys_addr,
1936 data: &mut mmio.data[0..mmio.len as usize],
1937 }
1938 }
1939 }
1940 KVM_EXIT_SHUTDOWN => Exit::Shutdown,
1941 #[cfg(target_arch = "x86_64")]
1942 KVM_EXIT_HYPERV => {
1943 let hyperv = unsafe { &mut self.run_data().__bindgen_anon_1.hyperv };
1945 match hyperv.type_ {
1946 KVM_EXIT_HYPERV_HCALL => {
1947 let hcall = unsafe { &mut hyperv.u.hcall };
1949 Exit::HvHypercall {
1950 input: hcall.input,
1951 result: &mut hcall.result,
1952 params: hcall.params,
1953 }
1954 }
1955 KVM_EXIT_HYPERV_SYNIC => {
1956 let synic = unsafe { &hyperv.u.synic };
1958 Exit::SynicUpdate {
1959 msr: synic.msr,
1960 control: synic.control,
1961 siefp: synic.evt_page,
1962 simp: synic.msg_page,
1963 }
1964 }
1965 _ => return Err(Error::UnknownHvExit(hyperv.type_)),
1966 }
1967 }
1968 #[cfg(target_arch = "x86_64")]
1969 KVM_EXIT_IOAPIC_EOI => {
1970 let eoi = unsafe { &mut self.run_data().__bindgen_anon_1.eoi };
1972
1973 Exit::Eoi { irq: eoi.vector }
1974 }
1975 KVM_EXIT_FAIL_ENTRY => {
1976 let fail_entry = unsafe { &self.run_data().__bindgen_anon_1.fail_entry };
1978 Exit::FailEntry {
1979 hardware_entry_failure_reason: fail_entry.hardware_entry_failure_reason,
1980 }
1981 }
1982 KVM_EXIT_INTERNAL_ERROR => {
1983 let internal = unsafe { &self.run_data().__bindgen_anon_1.internal };
1985 if internal.suberror == KVM_INTERNAL_ERROR_EMULATION {
1986 Exit::EmulationFailure {
1988 instruction_bytes: &[],
1989 }
1990 } else {
1991 Exit::InternalError {
1992 error: internal.suberror,
1993 data: &internal.data[..internal.ndata as usize],
1994 }
1995 }
1996 }
1997 #[cfg(target_arch = "x86_64")]
1998 KVM_EXIT_HYPERCALL => {
1999 let hypercall = unsafe { &mut self.run_data().__bindgen_anon_1.hypercall };
2001 Exit::Hypercall {
2002 nr: hypercall.nr,
2003 args: hypercall.args,
2004 result: &mut hypercall.ret,
2005 flags: unsafe { hypercall.__bindgen_anon_1.flags },
2007 }
2008 }
2009 #[cfg(target_arch = "x86_64")]
2010 KVM_EXIT_X86_WRMSR => {
2011 let msr = unsafe { &mut self.run_data().__bindgen_anon_1.msr };
2013 msr.error = 0;
2014 Exit::MsrWrite {
2015 index: msr.index,
2016 data: msr.data,
2017 error: &mut msr.error,
2018 }
2019 }
2020 #[cfg(target_arch = "x86_64")]
2021 KVM_EXIT_X86_RDMSR => {
2022 let msr = unsafe { &mut self.run_data().__bindgen_anon_1.msr };
2024 msr.data = 0;
2025 msr.error = 0;
2026 Exit::MsrRead {
2027 index: msr.index,
2028 data: &mut msr.data,
2029 error: &mut msr.error,
2030 }
2031 }
2032 KVM_EXIT_SYSTEM_EVENT => {
2033 let system_event = unsafe { &self.run_data().__bindgen_anon_1.system_event };
2035 Exit::SystemEvent {
2036 event_type: system_event.type_,
2037 event_flags: unsafe { system_event.__bindgen_anon_1.flags },
2039 }
2040 }
2041 exit_reason => return Err(Error::UnknownExit(exit_reason)),
2042 };
2043 Ok(exit)
2044 }
2045
2046 #[must_use]
2051 pub fn check_or_request_interrupt_window(&mut self) -> bool {
2052 let rdata = self.run_data();
2053 if rdata.ready_for_interrupt_injection != 0 {
2054 true
2055 } else {
2056 rdata.request_interrupt_window = 1;
2057 false
2058 }
2059 }
2060
2061 pub fn inject_extint_interrupt(&mut self, vector: u8) -> Result<()> {
2067 self.partition.vp(self.idx).interrupt(vector.into())?;
2068 self.run_data().ready_for_interrupt_injection = 0;
2071 Ok(())
2072 }
2073}
2074
2075#[derive(Debug)]
2076pub enum Exit<'a> {
2077 Interrupted,
2078 #[cfg(target_arch = "x86_64")]
2079 InterruptWindow,
2080 #[cfg(target_arch = "x86_64")]
2081 IoIn {
2082 port: u16,
2083 size: u8,
2084 data: &'a mut [u8],
2085 },
2086 #[cfg(target_arch = "x86_64")]
2087 IoOut {
2088 port: u16,
2089 size: u8,
2090 data: &'a [u8],
2091 },
2092 MmioRead {
2093 address: u64,
2094 data: &'a mut [u8],
2095 },
2096 MmioWrite {
2097 address: u64,
2098 data: &'a [u8],
2099 },
2100 #[cfg(target_arch = "x86_64")]
2101 Hypercall {
2102 nr: u64,
2103 args: [u64; 6],
2104 result: &'a mut u64,
2105 flags: u64,
2106 },
2107 #[cfg(target_arch = "x86_64")]
2108 MsrRead {
2109 index: u32,
2110 data: &'a mut u64,
2111 error: &'a mut u8,
2112 },
2113 #[cfg(target_arch = "x86_64")]
2114 MsrWrite {
2115 index: u32,
2116 data: u64,
2117 error: &'a mut u8,
2118 },
2119 Shutdown,
2120 FailEntry {
2121 hardware_entry_failure_reason: u64,
2122 },
2123 InternalError {
2124 error: u32,
2125 data: &'a [u64],
2126 },
2127 EmulationFailure {
2128 instruction_bytes: &'a [u8],
2129 },
2130 #[cfg(target_arch = "x86_64")]
2131 SynicUpdate {
2132 msr: u32,
2133 control: u64,
2134 siefp: u64,
2135 simp: u64,
2136 },
2137 #[cfg(target_arch = "x86_64")]
2138 HvHypercall {
2139 input: u64,
2140 result: &'a mut u64,
2141 params: [u64; 2],
2142 },
2143 #[cfg(target_arch = "x86_64")]
2144 Debug {
2145 exception: u32,
2146 pc: u64,
2147 dr6: u64,
2148 dr7: u64,
2149 },
2150 #[cfg(target_arch = "x86_64")]
2151 Eoi {
2152 irq: u8,
2153 },
2154 SystemEvent {
2155 event_type: u32,
2156 event_flags: u64,
2157 },
2158}
2159
2160pub fn init() {
2162 static SIGNAL_HANDLER_INIT: Once = Once::new();
2163 SIGNAL_HANDLER_INIT.call_once(|| {
2164 let handler = || {
2165 CURRENT_KVM_RUN.with(|run| {
2166 let rdata = run.load(Ordering::Relaxed);
2169 match rdata {
2170 NO_KVM_RUN => run.store(CANCEL_KVM_RUN, Ordering::Relaxed),
2171 CANCEL_KVM_RUN => {}
2172 _ => {
2173 unsafe { set_immediate_exit(rdata as *mut kvm_run) };
2176 }
2177 }
2178 })
2179 };
2180 CURRENT_KVM_RUN.with(|value| {
2182 std::hint::black_box(value);
2183 });
2184 unsafe {
2189 signal_hook::low_level::register(libc::SIGRTMIN(), handler).unwrap();
2190 }
2191 });
2192}
2193
2194const NO_KVM_RUN: usize = 0;
2195const CANCEL_KVM_RUN: usize = 1;
2196
2197thread_local! {
2198 static CURRENT_KVM_RUN: AtomicUsize = const { AtomicUsize::new(NO_KVM_RUN) };
2199}
2200
2201#[expect(clippy::missing_safety_doc)]
2206unsafe fn set_immediate_exit(rdata: *mut kvm_run) {
2207 unsafe {
2212 (*(std::ptr::addr_of!((*rdata).immediate_exit).cast::<AtomicU8>()))
2213 .store(1, Ordering::Relaxed);
2214 }
2215}
2216
2217pub struct DebugRegisters {
2218 pub db: [u64; 4],
2220 pub dr6: u64,
2221 pub dr7: u64,
2222}
2223
2224#[cfg(all(test, target_arch = "x86_64"))]
2225mod tests {
2226 use super::*;
2227
2228 #[test]
2229 fn sev_snp_page_type_values_match_kvm_uapi() {
2230 assert_eq!(SevSnpPageType::Normal.as_uapi(), 1);
2231 assert_eq!(SevSnpPageType::Zero.as_uapi(), 3);
2232 assert_eq!(SevSnpPageType::Unmeasured.as_uapi(), 4);
2233 assert_eq!(SevSnpPageType::Secrets.as_uapi(), 5);
2234 assert_eq!(SevSnpPageType::Cpuid.as_uapi(), 6);
2235 }
2236
2237 #[test]
2238 fn sev_snp_launch_update_uses_expected_zero_page_shape() {
2239 let update = kvm_sev_snp_launch_update {
2240 gfn_start: 0x1234,
2241 uaddr: 0,
2242 len: 0x2000,
2243 type_: SevSnpPageType::Zero.as_uapi(),
2244 ..Default::default()
2245 };
2246
2247 assert_eq!(update.gfn_start, 0x1234);
2248 assert_eq!(update.uaddr, 0);
2249 assert_eq!(update.len, 0x2000);
2250 assert_eq!(update.type_, KVM_SEV_SNP_PAGE_TYPE_ZERO_UAPI);
2251 assert_eq!(update.flags, 0);
2252 }
2253}